summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/cplusplus/CppDocument.cpp17
-rw-r--r--src/libs/cplusplus/CppDocument.h11
-rw-r--r--src/libs/cplusplus/FastPreprocessor.cpp1
-rw-r--r--src/libs/cplusplus/FastPreprocessor.h1
-rw-r--r--src/libs/cplusplus/Macro.cpp15
-rw-r--r--src/libs/cplusplus/Macro.h32
-rw-r--r--src/libs/cplusplus/MatchingText.cpp13
-rw-r--r--src/libs/cplusplus/PPToken.cpp45
-rw-r--r--src/libs/cplusplus/PPToken.h106
-rw-r--r--src/libs/cplusplus/PreprocessorClient.h4
-rw-r--r--src/libs/cplusplus/PreprocessorEnvironment.cpp28
-rw-r--r--src/libs/cplusplus/PreprocessorEnvironment.h5
-rw-r--r--src/libs/cplusplus/cplusplus-lib.pri8
-rw-r--r--src/libs/cplusplus/cplusplus.qbs8
-rw-r--r--src/libs/cplusplus/pp-engine.cpp1675
-rw-r--r--src/libs/cplusplus/pp-engine.h182
-rw-r--r--src/libs/cplusplus/pp-macro-expander.cpp427
-rw-r--r--src/libs/cplusplus/pp-macro-expander.h109
-rw-r--r--src/libs/cplusplus/pp.h1
-rw-r--r--src/libs/extensionsystem/iplugin.h15
-rw-r--r--src/libs/qmleditorwidgets/qmleditorwidgets-lib.pri8
-rw-r--r--src/libs/qmleditorwidgets/qmleditorwidgets.pri6
-rw-r--r--src/libs/qmljsdebugclient/qdeclarativeengineclient.h (renamed from src/tools/qmlprofilertool/commandlistener.h)31
-rw-r--r--src/libs/qmljsdebugclient/qdeclarativeenginedebug.cpp1097
-rw-r--r--src/libs/qmljsdebugclient/qdeclarativeenginedebug.h370
-rw-r--r--src/libs/qmljsdebugclient/qmldebuggerclient.cpp92
-rw-r--r--src/libs/qmljsdebugclient/qmldebuggerclient.h58
-rw-r--r--src/libs/qmljsdebugclient/qmlenginedebugclient.cpp415
-rw-r--r--src/libs/qmljsdebugclient/qmlenginedebugclient.h232
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri14
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient.pro3
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient.qbs12
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.cpp1887
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlocation.h2
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventtypes.h13
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp12
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.h6
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.cpp13
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.h8
-rw-r--r--src/libs/qtcomponents/styleitem/qstyleplugin.cpp2
-rw-r--r--src/libs/qtcomponents/styleitem/qstyleplugin.h5
-rw-r--r--src/libs/qtcomponents/styleitem/styleitem.json1
-rw-r--r--src/libs/symbianutils/symbianutils.qbs2
-rw-r--r--src/libs/utils/appmainwindow.cpp (renamed from src/plugins/coreplugin/eventfilteringmainwindow.cpp)31
-rw-r--r--src/libs/utils/appmainwindow.h (renamed from src/plugins/coreplugin/eventfilteringmainwindow.h)28
-rw-r--r--src/libs/utils/basetreeview.cpp137
-rw-r--r--src/libs/utils/basetreeview.h (renamed from src/plugins/qmljsinspector/qmljstoolbarcolorbox.h)59
-rw-r--r--src/libs/utils/basevalidatinglineedit.cpp13
-rw-r--r--src/libs/utils/basevalidatinglineedit.h1
-rw-r--r--src/libs/utils/crumblepath.cpp5
-rw-r--r--src/libs/utils/crumblepath.h1
-rw-r--r--src/libs/utils/filenamevalidatinglineedit.cpp24
-rw-r--r--src/libs/utils/filenamevalidatinglineedit.h10
-rw-r--r--src/libs/utils/filewizarddialog.cpp10
-rw-r--r--src/libs/utils/filewizarddialog.h3
-rw-r--r--src/libs/utils/filewizardpage.cpp11
-rw-r--r--src/libs/utils/filewizardpage.h3
-rw-r--r--src/libs/utils/iwelcomepage.h9
-rw-r--r--src/libs/utils/pathchooser.cpp10
-rw-r--r--src/libs/utils/pathchooser.h6
-rw-r--r--src/libs/utils/portlist.cpp (renamed from src/plugins/remotelinux/portlist.cpp)5
-rw-r--r--src/libs/utils/portlist.h (renamed from src/plugins/remotelinux/portlist.h)9
-rw-r--r--src/libs/utils/ssh/sftpfilesystemmodel.cpp4
-rw-r--r--src/libs/utils/ssh/sshconnection.cpp4
-rw-r--r--src/libs/utils/ssh/sshconnection.h2
-rw-r--r--src/libs/utils/ssh/sshkeycreationdialog.cpp (renamed from src/plugins/remotelinux/sshkeycreationdialog.cpp)15
-rw-r--r--src/libs/utils/ssh/sshkeycreationdialog.h (renamed from src/plugins/remotelinux/sshkeycreationdialog.h)20
-rw-r--r--src/libs/utils/ssh/sshkeycreationdialog.ui (renamed from src/plugins/remotelinux/sshkeycreationdialog.ui)6
-rw-r--r--src/libs/utils/tcpportsgatherer.cpp293
-rw-r--r--src/libs/utils/tcpportsgatherer.h (renamed from src/tools/qmlprofilertool/main.cpp)44
-rw-r--r--src/libs/utils/utils-lib.pri17
-rw-r--r--src/libs/utils/utils.pro2
-rw-r--r--src/libs/utils/utils.qbs17
-rw-r--r--src/libs/zeroconf/avahiLib.cpp32
-rw-r--r--src/libs/zeroconf/dnsSdLib.cpp7
-rw-r--r--src/libs/zeroconf/embed/CommonServices.h1518
-rw-r--r--src/libs/zeroconf/embed/DebugServices.c3075
-rw-r--r--src/libs/zeroconf/embed/DebugServices.h1607
-rw-r--r--src/libs/zeroconf/embed/dnssd_clientlib.c4
-rw-r--r--src/libs/zeroconf/embed/dnssd_clientstub.c46
-rw-r--r--src/libs/zeroconf/embed/dnssd_ipc.h2
-rw-r--r--src/libs/zeroconf/embeddedLib.cpp6
-rw-r--r--src/libs/zeroconf/servicebrowser.cpp382
-rw-r--r--src/libs/zeroconf/servicebrowser.h10
-rw-r--r--src/libs/zeroconf/servicebrowser_p.h18
-rw-r--r--src/libs/zeroconf/zeroconf.qbs3
-rw-r--r--src/pluginjsonmetadata.xsl27
-rw-r--r--src/plugins/QtcPlugin.qbs2
-rw-r--r--src/plugins/analyzerbase/AnalyzerBase.pluginspec.in1
-rw-r--r--src/plugins/analyzerbase/analyzerbase.qbs2
-rw-r--r--src/plugins/analyzerbase/analyzerbase_dependencies.pri1
-rw-r--r--src/plugins/analyzerbase/analyzerplugin.cpp7
-rw-r--r--src/plugins/analyzerbase/analyzerplugin.h1
-rw-r--r--src/plugins/analyzerbase/analyzerruncontrol.cpp12
-rw-r--r--src/plugins/analyzerbase/analyzerstartparameters.h1
-rw-r--r--src/plugins/analyzerbase/analyzerutils.cpp2
-rw-r--r--src/plugins/analyzerbase/startremotedialog.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/autogenstep.cpp4
-rw-r--r--src/plugins/autotoolsprojectmanager/autoreconfstep.cpp6
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsbuildconfiguration.cpp6
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsproject.cpp20
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsproject.h1
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.pro1
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h3
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolstarget.cpp4
-rw-r--r--src/plugins/autotoolsprojectmanager/configurestep.cpp6
-rw-r--r--src/plugins/autotoolsprojectmanager/makefileparser.cpp14
-rw-r--r--src/plugins/autotoolsprojectmanager/makestep.cpp2
-rw-r--r--src/plugins/bazaar/annotationhighlighter.cpp2
-rw-r--r--src/plugins/bazaar/bazaarclient.cpp10
-rw-r--r--src/plugins/bazaar/bazaarclient.h1
-rw-r--r--src/plugins/bazaar/bazaareditor.cpp27
-rw-r--r--src/plugins/bazaar/bazaareditor.h1
-rw-r--r--src/plugins/bazaar/bazaarplugin.h1
-rw-r--r--src/plugins/bazaar/constants.h9
-rw-r--r--src/plugins/bineditor/bineditorplugin.h1
-rw-r--r--src/plugins/bookmarks/bookmarksplugin.h1
-rw-r--r--src/plugins/classview/classview_dependencies.pri1
-rw-r--r--src/plugins/classview/classviewmanager.cpp2
-rw-r--r--src/plugins/classview/classviewparser.cpp2
-rw-r--r--src/plugins/classview/classviewparser.h2
-rw-r--r--src/plugins/classview/classviewplugin.h1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeeditor.cpp116
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeeditor.h10
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp33
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeeditorfactory.h2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.cpp8
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.h7
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectconstants.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp4
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectplugin.h1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketarget.cpp1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.h2
-rw-r--r--src/plugins/coreplugin/basefilewizard.cpp11
-rw-r--r--src/plugins/coreplugin/basefilewizard.h16
-rw-r--r--src/plugins/coreplugin/coreplugin.h1
-rw-r--r--src/plugins/coreplugin/coreplugin.pro8
-rw-r--r--src/plugins/coreplugin/coreplugin.qbs2
-rw-r--r--src/plugins/coreplugin/dialogs/iwizard.h3
-rw-r--r--src/plugins/coreplugin/fileiconprovider.cpp4
-rw-r--r--src/plugins/coreplugin/generalsettings.cpp12
-rw-r--r--src/plugins/coreplugin/generalsettings.h4
-rw-r--r--src/plugins/coreplugin/macfullscreen.h (renamed from src/tools/qmlprofilertool/constants.h)27
-rw-r--r--src/plugins/coreplugin/macfullscreen.mm123
-rw-r--r--src/plugins/coreplugin/mainwindow.cpp46
-rw-r--r--src/plugins/coreplugin/mainwindow.h8
-rw-r--r--src/plugins/coreplugin/textdocument.cpp7
-rw-r--r--src/plugins/coreplugin/textdocument.h1
-rw-r--r--src/plugins/cpaster/cpasterplugin.h1
-rw-r--r--src/plugins/cppeditor/cppcompleteswitch.cpp6
-rw-r--r--src/plugins/cppeditor/cppeditor.cpp7
-rw-r--r--src/plugins/cppeditor/cppeditor.h3
-rw-r--r--src/plugins/cppeditor/cppeditor.pro2
-rw-r--r--src/plugins/cppeditor/cppeditorconstants.h1
-rw-r--r--src/plugins/cppeditor/cppelementevaluator.cpp4
-rw-r--r--src/plugins/cppeditor/cppfunctiondecldeflink.cpp2
-rw-r--r--src/plugins/cppeditor/cpphoverhandler.cpp2
-rw-r--r--src/plugins/cppeditor/cppinsertdecldef.cpp8
-rw-r--r--src/plugins/cppeditor/cppplugin.cpp20
-rw-r--r--src/plugins/cppeditor/cppplugin.h2
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp2
-rw-r--r--src/plugins/cpptools/ModelManagerInterface.cpp (renamed from src/libs/cplusplus/ModelManagerInterface.cpp)0
-rw-r--r--src/plugins/cpptools/ModelManagerInterface.h (renamed from src/libs/cplusplus/ModelManagerInterface.h)20
-rw-r--r--src/plugins/cpptools/TypeHierarchyBuilder.cpp (renamed from src/libs/cplusplus/TypeHierarchyBuilder.cpp)0
-rw-r--r--src/plugins/cpptools/TypeHierarchyBuilder.h (renamed from src/libs/cplusplus/TypeHierarchyBuilder.h)5
-rw-r--r--src/plugins/cpptools/abstracteditorsupport.cpp2
-rw-r--r--src/plugins/cpptools/cppcodeformatter.cpp68
-rw-r--r--src/plugins/cpptools/cppcodeformatter.h11
-rw-r--r--src/plugins/cpptools/cppcompletionassist.cpp2
-rw-r--r--src/plugins/cpptools/cppfindreferences.cpp13
-rw-r--r--src/plugins/cpptools/cpphighlightingsupport.h2
-rw-r--r--src/plugins/cpptools/cpphighlightingsupportinternal.h3
-rw-r--r--src/plugins/cpptools/cpplocalsymbols.cpp2
-rw-r--r--src/plugins/cpptools/cppmodelmanager.cpp110
-rw-r--r--src/plugins/cpptools/cppmodelmanager.h12
-rw-r--r--src/plugins/cpptools/cpprefactoringchanges.h2
-rw-r--r--src/plugins/cpptools/cpptools.pro8
-rw-r--r--src/plugins/cpptools/cpptools.qbs10
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.h1
-rw-r--r--src/plugins/cpptools/uicodecompletionsupport.h2
-rw-r--r--src/plugins/cvs/cvsplugin.h1
-rw-r--r--src/plugins/debugger/Debugger.pluginspec.in1
-rw-r--r--src/plugins/debugger/basewindow.cpp112
-rw-r--r--src/plugins/debugger/basewindow.h33
-rw-r--r--src/plugins/debugger/breakwindow.cpp38
-rw-r--r--src/plugins/debugger/breakwindow.h10
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp2
-rw-r--r--src/plugins/debugger/commonoptionspage.cpp3
-rw-r--r--src/plugins/debugger/commonoptionspage.ui27
-rw-r--r--src/plugins/debugger/debuggeractions.cpp6
-rw-r--r--src/plugins/debugger/debuggeractions.h1
-rw-r--r--src/plugins/debugger/debuggerconstants.h6
-rw-r--r--src/plugins/debugger/debuggerengine.cpp39
-rw-r--r--src/plugins/debugger/debuggerengine.h15
-rw-r--r--src/plugins/debugger/debuggermainwindow.cpp51
-rw-r--r--src/plugins/debugger/debuggermainwindow.h7
-rw-r--r--src/plugins/debugger/debuggerplugin.cpp43
-rw-r--r--src/plugins/debugger/debuggerplugin.h1
-rw-r--r--src/plugins/debugger/debuggerrunner.cpp45
-rw-r--r--src/plugins/debugger/debuggerstartparameters.h5
-rw-r--r--src/plugins/debugger/gdb/abstractgdbadapter.h1
-rw-r--r--src/plugins/debugger/gdb/attachgdbadapter.cpp5
-rw-r--r--src/plugins/debugger/gdb/attachgdbadapter.h1
-rw-r--r--src/plugins/debugger/gdb/codagdbadapter.cpp5
-rw-r--r--src/plugins/debugger/gdb/codagdbadapter.h1
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp179
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h14
-rw-r--r--src/plugins/debugger/gdb/localplaingdbadapter.cpp5
-rw-r--r--src/plugins/debugger/gdb/localplaingdbadapter.h1
-rw-r--r--src/plugins/debugger/gdb/remotegdbserveradapter.cpp43
-rw-r--r--src/plugins/debugger/gdb/remotegdbserveradapter.h1
-rw-r--r--src/plugins/debugger/gdb/remoteplaingdbadapter.cpp5
-rw-r--r--src/plugins/debugger/gdb/remoteplaingdbadapter.h1
-rw-r--r--src/plugins/debugger/gdb/termgdbadapter.cpp5
-rw-r--r--src/plugins/debugger/gdb/termgdbadapter.h1
-rw-r--r--src/plugins/debugger/lldb/ipcenginehost.cpp2
-rw-r--r--src/plugins/debugger/lldb/ipcenginehost.h2
-rw-r--r--src/plugins/debugger/moduleswindow.cpp15
-rw-r--r--src/plugins/debugger/moduleswindow.h10
-rw-r--r--src/plugins/debugger/qml/qmladapter.cpp16
-rw-r--r--src/plugins/debugger/qml/qmladapter.h6
-rw-r--r--src/plugins/debugger/qml/qmlcppengine.cpp24
-rw-r--r--src/plugins/debugger/qml/qmlcppengine.h1
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp66
-rw-r--r--src/plugins/debugger/qml/qmlengine.h6
-rw-r--r--src/plugins/debugger/qml/qmljsprivateapi.h4
-rw-r--r--src/plugins/debugger/qtmessagelogeditor.cpp1
-rw-r--r--src/plugins/debugger/qtmessagelogitemdelegate.h2
-rw-r--r--src/plugins/debugger/qtmessagelogwindow.cpp13
-rw-r--r--src/plugins/debugger/registerwindow.cpp17
-rw-r--r--src/plugins/debugger/registerwindow.h10
-rw-r--r--src/plugins/debugger/snapshotwindow.cpp10
-rw-r--r--src/plugins/debugger/snapshotwindow.h14
-rw-r--r--src/plugins/debugger/sourcefileswindow.cpp15
-rw-r--r--src/plugins/debugger/sourcefileswindow.h10
-rw-r--r--src/plugins/debugger/stackframe.cpp21
-rw-r--r--src/plugins/debugger/stackhandler.cpp2
-rw-r--r--src/plugins/debugger/stackwindow.cpp22
-rw-r--r--src/plugins/debugger/stackwindow.h13
-rw-r--r--src/plugins/debugger/threadswindow.cpp20
-rw-r--r--src/plugins/debugger/threadswindow.h10
-rw-r--r--src/plugins/debugger/watchutils.cpp2
-rw-r--r--src/plugins/debugger/watchwindow.cpp71
-rw-r--r--src/plugins/debugger/watchwindow.h15
-rw-r--r--src/plugins/designer/codemodelhelpers.cpp2
-rw-r--r--src/plugins/designer/formeditorplugin.h1
-rw-r--r--src/plugins/designer/qtcreatorintegration.cpp2
-rw-r--r--src/plugins/designer/qtcreatorintegration.h2
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp4
-rw-r--r--src/plugins/fakevim/fakevimplugin.h1
-rw-r--r--src/plugins/find/findplugin.h1
-rw-r--r--src/plugins/find/treeviewfind.cpp71
-rw-r--r--src/plugins/find/treeviewfind.h3
-rw-r--r--src/plugins/genericprojectmanager/genericproject.cpp7
-rw-r--r--src/plugins/genericprojectmanager/genericproject.h2
-rw-r--r--src/plugins/genericprojectmanager/genericprojectplugin.h1
-rw-r--r--src/plugins/git/commitdata.cpp4
-rw-r--r--src/plugins/git/commitdata.h1
-rw-r--r--src/plugins/git/gitclient.cpp20
-rw-r--r--src/plugins/git/gitclient.h4
-rw-r--r--src/plugins/git/gitplugin.cpp14
-rw-r--r--src/plugins/git/gitplugin.h3
-rw-r--r--src/plugins/git/gitsettings.cpp2
-rw-r--r--src/plugins/git/gitsettings.h1
-rw-r--r--src/plugins/git/gitsubmiteditorwidget.cpp2
-rw-r--r--src/plugins/git/gitsubmitpanel.ui75
-rw-r--r--src/plugins/git/settingspage.cpp7
-rw-r--r--src/plugins/git/settingspage.ui38
-rw-r--r--src/plugins/glsleditor/glsleditorplugin.h1
-rw-r--r--src/plugins/helloworld/helloworldplugin.h1
-rw-r--r--src/plugins/help/helpplugin.cpp2
-rw-r--r--src/plugins/help/helpplugin.h1
-rw-r--r--src/plugins/imageviewer/imageviewerplugin.h1
-rw-r--r--src/plugins/locator/locatorplugin.h5
-rw-r--r--src/plugins/macros/macrosplugin.h1
-rw-r--r--src/plugins/madde/maddedeviceconfigurationfactory.cpp44
-rw-r--r--src/plugins/madde/maddedeviceconfigurationfactory.h18
-rw-r--r--src/plugins/madde/maddedevicetester.cpp8
-rw-r--r--src/plugins/madde/maddeplugin.h2
-rw-r--r--src/plugins/madde/maddeuploadandinstallpackagesteps.cpp2
-rw-r--r--src/plugins/madde/maemodeploybymountsteps.cpp2
-rw-r--r--src/plugins/madde/maemodeployconfigurationwidget.cpp4
-rw-r--r--src/plugins/madde/maemodeploymentmounter.cpp2
-rw-r--r--src/plugins/madde/maemodeploymentmounter.h4
-rw-r--r--src/plugins/madde/maemodeviceconfigwizard.cpp70
-rw-r--r--src/plugins/madde/maemodeviceconfigwizard.h7
-rw-r--r--src/plugins/madde/maemoglobal.cpp27
-rw-r--r--src/plugins/madde/maemoglobal.h24
-rw-r--r--src/plugins/madde/maemopublisherfremantlefree.cpp1
-rw-r--r--src/plugins/madde/maemopublishingbuildsettingspagefremantlefree.cpp2
-rw-r--r--src/plugins/madde/maemopublishingwizardfactories.cpp2
-rw-r--r--src/plugins/madde/maemoqemumanager.cpp2
-rw-r--r--src/plugins/madde/maemoqemuruntime.h4
-rw-r--r--src/plugins/madde/maemoqemuruntimeparser.cpp2
-rw-r--r--src/plugins/madde/maemoqtversion.cpp30
-rw-r--r--src/plugins/madde/maemoqtversion.h4
-rw-r--r--src/plugins/madde/maemoremotecopyfacility.cpp2
-rw-r--r--src/plugins/madde/maemoremotemounter.cpp8
-rw-r--r--src/plugins/madde/maemoremotemounter.h6
-rw-r--r--src/plugins/madde/maemorunconfiguration.cpp8
-rw-r--r--src/plugins/madde/maemorunconfiguration.h2
-rw-r--r--src/plugins/madde/maemorunconfigurationwidget.cpp11
-rw-r--r--src/plugins/madde/maemorunconfigurationwidget.h2
-rw-r--r--src/plugins/madde/maemosettingspages.cpp4
-rw-r--r--src/plugins/madde/maemosshrunner.cpp2
-rw-r--r--src/plugins/madde/qt4maemotarget.cpp39
-rw-r--r--src/plugins/madde/qt4maemotarget.h11
-rw-r--r--src/plugins/mercurial/mercurialplugin.h1
-rw-r--r--src/plugins/perforce/perforceplugin.h1
-rw-r--r--src/plugins/plugins.pro8
-rw-r--r--src/plugins/projectexplorer/buildmanager.cpp6
-rw-r--r--src/plugins/projectexplorer/buildmanager.h2
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.cpp58
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.h9
-rw-r--r--src/plugins/projectexplorer/deployconfiguration.cpp10
-rw-r--r--src/plugins/projectexplorer/deployconfiguration.h6
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp (renamed from src/plugins/remotelinux/linuxdevicefactoryselectiondialog.cpp)27
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.h (renamed from src/plugins/remotelinux/linuxdevicefactoryselectiondialog.h)26
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.ui (renamed from src/plugins/remotelinux/linuxdevicefactoryselectiondialog.ui)8
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp431
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.h111
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp114
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanagermodel.h67
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp (renamed from src/plugins/remotelinux/remotelinuxsettingspages.cpp)47
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingspage.h (renamed from src/plugins/remotelinux/remotelinuxsettingspages.h)20
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp (renamed from src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.cpp)163
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.h (renamed from src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.h)49
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.ui (renamed from src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.ui)122
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp221
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h89
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicefactory.h129
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicewidget.h68
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicewizard.h64
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.cpp12
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.h4
-rw-r--r--src/plugins/projectexplorer/iprojectproperties.h2
-rw-r--r--src/plugins/projectexplorer/project.cpp5
-rw-r--r--src/plugins/projectexplorer/project.h3
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp36
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h6
-rw-r--r--src/plugins/projectexplorer/projectexplorer.pro33
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qbs19
-rw-r--r--src/plugins/projectexplorer/projectexplorerconstants.h4
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.cpp7
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.h1
-rw-r--r--src/plugins/projectexplorer/projectwindow.cpp4
-rw-r--r--src/plugins/projectexplorer/projectwindow.h2
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.cpp27
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp15
-rw-r--r--src/plugins/projectexplorer/runconfiguration.h10
-rw-r--r--src/plugins/projectexplorer/target.cpp21
-rw-r--r--src/plugins/projectexplorer/target.h9
-rw-r--r--src/plugins/projectexplorer/taskhub.cpp5
-rw-r--r--src/plugins/projectexplorer/taskhub.h2
-rw-r--r--src/plugins/projectexplorer/taskwindow.cpp10
-rw-r--r--src/plugins/projectexplorer/taskwindow.h1
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.h1
-rw-r--r--src/plugins/qmljseditor/qmlfilewizard.cpp4
-rw-r--r--src/plugins/qmljseditor/qmljseditor.cpp5
-rw-r--r--src/plugins/qmljseditor/qmljseditor.h1
-rw-r--r--src/plugins/qmljseditor/qmljseditorconstants.h1
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.cpp24
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.h2
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.cpp4
-rw-r--r--src/plugins/qmljsinspector/images/color-picker-small-hicontrast.pngbin3206 -> 0 bytes
-rw-r--r--src/plugins/qmljsinspector/images/color-picker-small.pngbin3173 -> 0 bytes
-rw-r--r--src/plugins/qmljsinspector/images/color-picker.pngbin3185 -> 0 bytes
-rw-r--r--src/plugins/qmljsinspector/qmljsclientproxy.cpp430
-rw-r--r--src/plugins/qmljsinspector/qmljsclientproxy.h71
-rw-r--r--src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp2
-rw-r--r--src/plugins/qmljsinspector/qmljsinspector.cpp326
-rw-r--r--src/plugins/qmljsinspector/qmljsinspector.h34
-rw-r--r--src/plugins/qmljsinspector/qmljsinspector.pro9
-rw-r--r--src/plugins/qmljsinspector/qmljsinspector.qbs2
-rw-r--r--src/plugins/qmljsinspector/qmljsinspector.qrc3
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorclient.cpp43
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorclient.h5
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorconstants.h2
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorplugin.h1
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectortoolbar.cpp114
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectortoolbar.h24
-rw-r--r--src/plugins/qmljsinspector/qmljslivetextpreview.cpp46
-rw-r--r--src/plugins/qmljsinspector/qmljslivetextpreview.h11
-rw-r--r--src/plugins/qmljsinspector/qmljsprivateapi.h4
-rw-r--r--src/plugins/qmljsinspector/qmljspropertyinspector.cpp148
-rw-r--r--src/plugins/qmljsinspector/qmljspropertyinspector.h46
-rw-r--r--src/plugins/qmljsinspector/qmljstoolbarcolorbox.cpp132
-rw-r--r--src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp27
-rw-r--r--src/plugins/qmljstools/qmljsmodelmanager.cpp2
-rw-r--r--src/plugins/qmljstools/qmljstools.qbs4
-rw-r--r--src/plugins/qmljstools/qmljstools_dependencies.pri1
-rw-r--r--src/plugins/qmljstools/qmljstoolsplugin.h1
-rw-r--r--src/plugins/qmlprofiler/abstractqmlprofilerrunner.h2
-rw-r--r--src/plugins/qmlprofiler/codaqmlprofilerrunner.cpp2
-rw-r--r--src/plugins/qmlprofiler/codaqmlprofilerrunner.h2
-rw-r--r--src/plugins/qmlprofiler/localqmlprofilerrunner.cpp2
-rw-r--r--src/plugins/qmlprofiler/localqmlprofilerrunner.h2
-rw-r--r--src/plugins/qmlprofiler/qml/Label.qml18
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.qml157
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.js51
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.qml16
-rw-r--r--src/plugins/qmlprofiler/qml/SelectionRange.qml2
-rw-r--r--src/plugins/qmlprofiler/qml/StatusDisplay.qml8
-rw-r--r--src/plugins/qmlprofiler/qml/TimeMarks.qml14
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.pro33
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.qbs23
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp433
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.h103
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerconstants.h1
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp1667
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.h (renamed from src/libs/qmljsdebugclient/qmlprofilereventlist.h)137
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerengine.cpp189
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerengine.h17
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp302
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.h48
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerplugin.h1
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp163
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.h82
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp633
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.h54
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.cpp601
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.h (renamed from src/plugins/qmlprofiler/tracewindow.h)116
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp172
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.h79
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.cpp445
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.h79
-rw-r--r--src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.cpp8
-rw-r--r--src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.h4
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.cpp (renamed from src/plugins/qmlprofiler/timelineview.cpp)216
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.h (renamed from src/plugins/qmlprofiler/timelineview.h)28
-rw-r--r--src/plugins/qmlprofiler/tracewindow.cpp666
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp5
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.pro7
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.h1
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp2
-rw-r--r--src/plugins/qt4projectmanager/profileeditor.cpp5
-rw-r--r--src/plugins/qt4projectmanager/profileeditor.h1
-rw-r--r--src/plugins/qt4projectmanager/qt-desktop/desktopqtversion.cpp1
-rw-r--r--src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.cpp11
-rw-r--r--src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.cpp2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp9
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp11
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/sbsv2parser.cpp5
-rw-r--r--src/plugins/qt4projectmanager/qt4buildconfiguration.cpp47
-rw-r--r--src/plugins/qt4projectmanager/qt4buildconfiguration.h14
-rw-r--r--src/plugins/qt4projectmanager/qt4nodes.cpp25
-rw-r--r--src/plugins/qt4projectmanager/qt4project.cpp42
-rw-r--r--src/plugins/qt4projectmanager/qt4project.h5
-rw-r--r--src/plugins/qt4projectmanager/qt4projectmanagerconstants.h1
-rw-r--r--src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp18
-rw-r--r--src/plugins/qt4projectmanager/qt4projectmanagerplugin.h2
-rw-r--r--src/plugins/qt4projectmanager/qtmodulesinfo.cpp37
-rw-r--r--src/plugins/qt4projectmanager/unconfiguredprojectpanel.cpp5
-rw-r--r--src/plugins/qt4projectmanager/unconfiguredprojectpanel.h2
-rw-r--r--src/plugins/qtsupport/baseqtversion.cpp15
-rw-r--r--src/plugins/qtsupport/exampleslistmodel.cpp25
-rw-r--r--src/plugins/qtsupport/exampleslistmodel.h3
-rw-r--r--src/plugins/qtsupport/gettingstartedwelcomepage.cpp35
-rw-r--r--src/plugins/qtsupport/gettingstartedwelcomepage.h4
-rw-r--r--src/plugins/qtsupport/qtoptionspage.cpp114
-rw-r--r--src/plugins/qtsupport/qtoptionspage.h6
-rw-r--r--src/plugins/qtsupport/qtoutputformatter.cpp8
-rw-r--r--src/plugins/qtsupport/qtoutputformatter.h2
-rw-r--r--src/plugins/qtsupport/qtsupport.pro8
-rw-r--r--src/plugins/qtsupport/qtsupportplugin.h1
-rw-r--r--src/plugins/qtsupport/qtversionmanager.cpp30
-rw-r--r--src/plugins/qtsupport/qtversionmanager.h8
-rw-r--r--src/plugins/qtsupport/screenshotcropper.cpp25
-rw-r--r--src/plugins/qtsupport/screenshotcropper.h6
-rw-r--r--src/plugins/remotelinux/abstractembeddedlinuxtarget.cpp6
-rw-r--r--src/plugins/remotelinux/abstractembeddedlinuxtarget.h15
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp2
-rw-r--r--src/plugins/remotelinux/deployablefilesperprofile.cpp9
-rw-r--r--src/plugins/remotelinux/deployablefilesperprofile.h2
-rw-r--r--src/plugins/remotelinux/deploymentinfo.cpp16
-rw-r--r--src/plugins/remotelinux/deploymentinfo.h3
-rw-r--r--src/plugins/remotelinux/genericembeddedlinuxtarget.cpp9
-rw-r--r--src/plugins/remotelinux/genericembeddedlinuxtarget.h1
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.cpp50
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.h19
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp56
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h12
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui42
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp15
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h8
-rw-r--r--src/plugins/remotelinux/linuxdeviceconfiguration.cpp212
-rw-r--r--src/plugins/remotelinux/linuxdeviceconfiguration.h191
-rw-r--r--src/plugins/remotelinux/linuxdeviceconfigurations.cpp399
-rw-r--r--src/plugins/remotelinux/linuxdeviceconfigurations.h113
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp2
-rw-r--r--src/plugins/remotelinux/remotelinux.pro15
-rw-r--r--src/plugins/remotelinux/remotelinux.qbs15
-rw-r--r--src/plugins/remotelinux/remotelinux_constants.h3
-rw-r--r--src/plugins/remotelinux/remotelinuxapplicationrunner.cpp4
-rw-r--r--src/plugins/remotelinux/remotelinuxapplicationrunner.h4
-rw-r--r--src/plugins/remotelinux/remotelinuxdebugsupport.cpp12
-rw-r--r--src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp11
-rw-r--r--src/plugins/remotelinux/remotelinuxdeployconfigurationwidget.cpp12
-rw-r--r--src/plugins/remotelinux/remotelinuxplugin.cpp5
-rw-r--r--src/plugins/remotelinux/remotelinuxplugin.h1
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfiguration.cpp31
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfiguration.h11
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfigurationwidget.cpp16
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfigurationwidget.h2
-rw-r--r--src/plugins/remotelinux/remotelinuxruncontrolfactory.cpp2
-rw-r--r--src/plugins/remotelinux/remotelinuxusedportsgatherer.cpp96
-rw-r--r--src/plugins/remotelinux/remotelinuxusedportsgatherer.h15
-rw-r--r--src/plugins/remotelinux/remotelinuxutils.cpp46
-rw-r--r--src/plugins/remotelinux/remotelinuxutils.h6
-rw-r--r--src/plugins/remotelinux/startgdbserverdialog.cpp47
-rw-r--r--src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.cpp76
-rw-r--r--src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.h8
-rw-r--r--src/plugins/resourceeditor/resourceeditorplugin.h1
-rw-r--r--src/plugins/subversion/subversionplugin.h1
-rw-r--r--src/plugins/tasklist/tasklistplugin.h1
-rw-r--r--src/plugins/texteditor/autocompleter.cpp7
-rw-r--r--src/plugins/texteditor/basetexteditor.cpp21
-rw-r--r--src/plugins/texteditor/basetexteditor.h3
-rw-r--r--src/plugins/texteditor/circularclipboard.cpp10
-rw-r--r--src/plugins/texteditor/codeassist/genericproposalwidget.cpp4
-rw-r--r--src/plugins/texteditor/texteditor.qbs4
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.cpp22
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.h9
-rw-r--r--src/plugins/texteditor/texteditorconstants.h3
-rw-r--r--src/plugins/texteditor/texteditorplugin.cpp56
-rw-r--r--src/plugins/texteditor/texteditorplugin.h1
-rw-r--r--src/plugins/todo/Todo.pluginspec.in1
-rw-r--r--src/plugins/todo/cpptodoitemsscanner.h2
-rwxr-xr-xsrc/plugins/todo/todoplugin.h2
-rw-r--r--src/plugins/updateinfo/updateinfoplugin.h2
-rw-r--r--src/plugins/valgrind/callgrindtextmark.cpp4
-rw-r--r--src/plugins/valgrind/valgrindplugin.h1
-rw-r--r--src/plugins/valgrind/valgrindrunner.cpp3
-rw-r--r--src/plugins/vcsbase/VcsBase.pluginspec.in1
-rw-r--r--src/plugins/vcsbase/vcsbase.qbs1
-rw-r--r--src/plugins/vcsbase/vcsbase_dependencies.pri2
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp8
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.cpp2
-rw-r--r--src/plugins/vcsbase/vcsplugin.h1
-rw-r--r--src/plugins/welcome/welcome.pro7
-rw-r--r--src/plugins/welcome/welcomeplugin.cpp46
-rw-r--r--src/plugins/welcome/welcomeplugin.h2
-rw-r--r--src/qtcreatorplugin.pri16
-rw-r--r--src/shared/help/bookmarkmanager.cpp6
-rw-r--r--src/shared/help/bookmarkmanager.h1
-rw-r--r--src/tools/mdnssd/CommonServices.h1518
-rw-r--r--src/tools/mdnssd/DebugServices.c3075
-rw-r--r--src/tools/mdnssd/EventLog.mc11
-rw-r--r--src/tools/mdnssd/Firewall.cpp484
-rw-r--r--src/tools/mdnssd/Firewall.h79
-rw-r--r--src/tools/mdnssd/LegacyNATTraversal.c906
-rw-r--r--src/tools/mdnssd/Poll.c728
-rw-r--r--src/tools/mdnssd/Poll.h61
-rw-r--r--src/tools/mdnssd/PosixDaemon.c124
-rw-r--r--src/tools/mdnssd/RegNames.h57
-rw-r--r--src/tools/mdnssd/Secret.c338
-rw-r--r--src/tools/mdnssd/Secret.h42
-rw-r--r--src/tools/mdnssd/Service.c2597
-rw-r--r--src/tools/mdnssd/Service.h30
-rw-r--r--src/tools/mdnssd/Service.rc115
-rw-r--r--src/tools/mdnssd/mDNS.c34
-rwxr-xr-xsrc/tools/mdnssd/mDNSEmbeddedAPI.h8
-rwxr-xr-xsrc/tools/mdnssd/mDNSPosix.c35
-rw-r--r--src/tools/mdnssd/mDNSWin32.c5018
-rw-r--r--src/tools/mdnssd/mDNSWin32.h163
-rw-r--r--src/tools/mdnssd/main.c38
-rw-r--r--src/tools/mdnssd/mdnssd.pro75
-rw-r--r--src/tools/mdnssd/resource.h17
-rwxr-xr-xsrc/tools/mdnssd/uDNS.c12
-rw-r--r--src/tools/mdnssd/uds_daemon.c8218
-rw-r--r--src/tools/qmlprofilertool/commandlistener.cpp57
-rw-r--r--src/tools/qmlprofilertool/qmlprofilerapplication.h113
-rw-r--r--src/tools/qmlprofilertool/qmlprofilertool.pro28
-rw-r--r--src/tools/screenshotcropper/screenshotcropperwindow.h2
-rw-r--r--src/tools/tools.pro2
584 files changed, 39153 insertions, 14723 deletions
diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index ec617ff929..0d39e3b1db 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -198,6 +198,8 @@ protected:
};
+#define DO_NOT_DUMP_ALL_PARSER_ERRORS
+
class DocumentDiagnosticClient : public DiagnosticClient
{
enum { MAX_MESSAGE_COUNT = 10 };
@@ -217,8 +219,10 @@ public:
if (level == Error) {
++errorCount;
+#ifdef DO_NOT_DUMP_ALL_PARSER_ERRORS
if (errorCount >= MAX_MESSAGE_COUNT)
return; // ignore the error
+#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
}
const QString fileName = QString::fromUtf8(fileId->chars(), fileId->size());
@@ -229,6 +233,16 @@ public:
QString message;
message.vsprintf(format, ap);
+#ifndef DO_NOT_DUMP_ALL_PARSER_ERRORS
+ {
+ const char *levelStr = "Unknown level";
+ if (level == Document::DiagnosticMessage::Warning) levelStr = "Warning";
+ if (level == Document::DiagnosticMessage::Error) levelStr = "Error";
+ if (level == Document::DiagnosticMessage::Fatal) levelStr = "Fatal";
+ qDebug("%s:%u:%u: %s: %s", fileId->chars(), line, column, levelStr, message.toUtf8().constData());
+ }
+#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
+
Document::DiagnosticMessage m(convertLevel(level), doc->fileName(),
line, column, message);
messages->append(m);
@@ -341,10 +355,9 @@ void Document::appendMacro(const Macro &macro)
void Document::addMacroUse(const Macro &macro, unsigned offset, unsigned length,
unsigned beginLine,
- const QVector<MacroArgumentReference> &actuals, bool inCondition)
+ const QVector<MacroArgumentReference> &actuals)
{
MacroUse use(macro, offset, offset + length, beginLine);
- use.setInCondition(inCondition);
foreach (const MacroArgumentReference &actual, actuals) {
const Block arg(actual.position(), actual.position() + actual.length());
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 258d86138b..4147a95c34 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -77,8 +77,7 @@ public:
void appendMacro(const Macro &macro);
void addMacroUse(const Macro &macro, unsigned offset, unsigned length,
- unsigned beginLine, const QVector<MacroArgumentReference> &range,
- bool inCondition);
+ unsigned beginLine, const QVector<MacroArgumentReference> &range);
void addUndefinedMacroUse(const QByteArray &name, unsigned offset);
Control *control() const;
@@ -247,7 +246,6 @@ public:
class MacroUse: public Block {
Macro _macro;
QVector<Block> _arguments;
- bool _inCondition;
unsigned _beginLine;
public:
@@ -255,7 +253,6 @@ public:
unsigned begin, unsigned end, unsigned beginLine)
: Block(begin, end),
_macro(macro),
- _inCondition(false),
_beginLine(beginLine)
{ }
@@ -268,9 +265,6 @@ public:
QVector<Block> arguments() const
{ return _arguments; }
- bool isInCondition() const
- { return _inCondition; }
-
unsigned beginLine() const
{ return _beginLine; }
@@ -281,9 +275,6 @@ public:
void addArgument(const Block &block)
{ _arguments.append(block); }
- void setInCondition(bool set)
- { _inCondition = set; }
-
friend class Document;
};
diff --git a/src/libs/cplusplus/FastPreprocessor.cpp b/src/libs/cplusplus/FastPreprocessor.cpp
index ff8896283b..fe50fa334d 100644
--- a/src/libs/cplusplus/FastPreprocessor.cpp
+++ b/src/libs/cplusplus/FastPreprocessor.cpp
@@ -56,6 +56,7 @@ QByteArray FastPreprocessor::run(QString fileName, const QString &source)
}
const QByteArray preprocessed = _preproc(fileName, source);
+// qDebug("FastPreprocessor::run for %s produced [[%s]]", fileName.toUtf8().constData(), preprocessed.constData());
return preprocessed;
}
diff --git a/src/libs/cplusplus/FastPreprocessor.h b/src/libs/cplusplus/FastPreprocessor.h
index b032b6af27..85a8e8c630 100644
--- a/src/libs/cplusplus/FastPreprocessor.h
+++ b/src/libs/cplusplus/FastPreprocessor.h
@@ -69,7 +69,6 @@ public:
virtual void startExpandingMacro(unsigned,
const Macro &,
const QByteArray &,
- bool,
const QVector<MacroArgumentReference> &) {}
virtual void stopExpandingMacro(unsigned, const Macro &) {}
diff --git a/src/libs/cplusplus/Macro.cpp b/src/libs/cplusplus/Macro.cpp
index 802f126e2a..c3390f06fd 100644
--- a/src/libs/cplusplus/Macro.cpp
+++ b/src/libs/cplusplus/Macro.cpp
@@ -78,7 +78,8 @@ QString Macro::decoratedName() const
text += QLatin1String(", ");
else
first = false;
- text += QString::fromUtf8(formal.constData(), formal.size());
+ if (formal != "__VA_ARGS__")
+ text += QString::fromUtf8(formal.constData(), formal.size());
}
if (f._variadic)
text += QLatin1String("...");
@@ -91,19 +92,11 @@ QString Macro::decoratedName() const
QString Macro::toString() const
{
QString text = decoratedName();
- text.append(QString::fromUtf8(_definition.constData(), _definition.size()));
+ text.append(QString::fromUtf8(_definitionText.constData(), _definitionText.size()));
return text;
}
QString Macro::toStringWithLineBreaks() const
{
- if (_lineBreaks.isEmpty())
- return toString();
-
- QString text = decoratedName();
- QString definitionWithBreaks = QString::fromUtf8(_definition.constData(), _definition.size());
- foreach (unsigned pos, _lineBreaks)
- definitionWithBreaks[pos] = '\n';
- text.append(definitionWithBreaks);
- return text;
+ return toString();
}
diff --git a/src/libs/cplusplus/Macro.h b/src/libs/cplusplus/Macro.h
index 004969333f..56de90fcf4 100644
--- a/src/libs/cplusplus/Macro.h
+++ b/src/libs/cplusplus/Macro.h
@@ -52,6 +52,8 @@
#ifndef CPLUSPLUS_PP_MACRO_H
#define CPLUSPLUS_PP_MACRO_H
+#include "PPToken.h"
+
#include <CPlusPlusForwardDeclarations.h>
#include <QByteArray>
@@ -60,8 +62,12 @@
namespace CPlusPlus {
+class Environment;
+
class CPLUSPLUS_EXPORT Macro
{
+ typedef Internal::PPToken PPToken;
+
public:
Macro();
@@ -71,13 +77,16 @@ public:
void setName(const QByteArray &name)
{ _name = name; }
- QByteArray definition() const
- { return _definition; }
+ const QByteArray definitionText() const
+ { return _definitionText; }
+
+ const QVector<PPToken> &definitionTokens() const
+ { return _definitionTokens; }
- void setDefinition(const QByteArray &definition)
- { _definition = definition; }
+ void setDefinition(const QByteArray &definitionText, const QVector<PPToken> &definitionTokens)
+ { _definitionText = definitionText; _definitionTokens = definitionTokens; }
- QVector<QByteArray> formals() const
+ const QVector<QByteArray> &formals() const
{ return _formals; }
void addFormal(const QByteArray &formal)
@@ -125,16 +134,11 @@ public:
void setVariadic(bool isVariadic)
{ f._variadic = isVariadic; }
- void setLineBreaks(const QList<unsigned> &breaks)
- { _lineBreaks = breaks; }
-
- const QList<unsigned> &lineBreaks() const
- { return _lineBreaks; }
-
QString toString() const;
QString toStringWithLineBreaks() const;
-// ### private
+private:
+ friend class Environment;
Macro *_next;
unsigned _hashcode;
@@ -149,10 +153,10 @@ private:
};
QByteArray _name;
- QByteArray _definition;
+ QByteArray _definitionText;
+ QVector<PPToken> _definitionTokens;
QVector<QByteArray> _formals;
QString _fileName;
- QList<unsigned> _lineBreaks;
unsigned _line;
unsigned _offset;
unsigned _length;
diff --git a/src/libs/cplusplus/MatchingText.cpp b/src/libs/cplusplus/MatchingText.cpp
index c0847bc595..d4b3566ad9 100644
--- a/src/libs/cplusplus/MatchingText.cpp
+++ b/src/libs/cplusplus/MatchingText.cpp
@@ -120,6 +120,7 @@ QString MatchingText::insertMatchingBrace(const QTextCursor &cursor, const QStri
QString text = textToProcess;
const QString blockText = tc.block().text().mid(tc.positionInBlock());
+ const QString trimmedBlockText = blockText.trimmed();
const int length = qMin(blockText.length(), textToProcess.length());
const QChar previousChar = doc->characterAt(tc.selectionEnd() - 1);
@@ -190,10 +191,14 @@ QString MatchingText::insertMatchingBrace(const QTextCursor &cursor, const QStri
QString result;
foreach (const QChar &ch, text) {
- if (ch == QLatin1Char('(')) result += ')';
- else if (ch == QLatin1Char('[')) result += ']';
- else if (ch == QLatin1Char('"')) result += '"';
- else if (ch == QLatin1Char('\'')) result += '\'';
+ if (ch == QLatin1Char('(')) result += QLatin1Char(')');
+ else if (ch == QLatin1Char('[')) result += QLatin1Char(']');
+ else if (ch == QLatin1Char('"')) result += QLatin1Char('"');
+ else if (ch == QLatin1Char('\'')) result += QLatin1Char('\'');
+ // Handle '{' appearance within functinon call context
+ else if (ch == QLatin1Char('{') && !trimmedBlockText.isEmpty() && trimmedBlockText.at(0) == QLatin1Char(')'))
+ result += QLatin1Char('}');
+
}
return result;
diff --git a/src/libs/cplusplus/PPToken.cpp b/src/libs/cplusplus/PPToken.cpp
new file mode 100644
index 0000000000..80577ab6cf
--- /dev/null
+++ b/src/libs/cplusplus/PPToken.cpp
@@ -0,0 +1,45 @@
+#include "PPToken.h"
+
+#include <cstring>
+
+using namespace CPlusPlus::Internal;
+
+ByteArrayRef::ByteArrayRef()
+ : m_ref(0)
+ , m_offset(0)
+ , m_length(0)
+{}
+
+bool ByteArrayRef::startsWith(const char *s) const
+{
+ int l = std::strlen(s);
+ if (l > m_length)
+ return false;
+ return !qstrncmp(start(), s, l);
+}
+
+int ByteArrayRef::count(char ch) const
+{
+ if (!m_ref)
+ return 0;
+
+ int num = 0;
+ const char *b = start();
+ const char *i = b + m_length;
+ while (i != b)
+ if (*--i == ch)
+ ++num;
+ return num;
+}
+
+PPToken::PPToken()
+{}
+
+void PPToken::squeeze()
+{
+ if (isValid()) {
+ m_src = m_src.mid(offset, length());
+ m_src.squeeze();
+ offset = 0;
+ }
+}
diff --git a/src/libs/cplusplus/PPToken.h b/src/libs/cplusplus/PPToken.h
new file mode 100644
index 0000000000..82076872ef
--- /dev/null
+++ b/src/libs/cplusplus/PPToken.h
@@ -0,0 +1,106 @@
+#ifndef CPLUSPLUS_INTERNAL_PPTOKEN_H
+#define CPLUSPLUS_INTERNAL_PPTOKEN_H
+
+#include <CPlusPlus.h>
+#include <Token.h>
+
+#include <QByteArray>
+
+namespace CPlusPlus {
+namespace Internal {
+
+class CPLUSPLUS_EXPORT ByteArrayRef
+{
+public:
+ ByteArrayRef();
+
+ ByteArrayRef(const QByteArray *ref)
+ : m_ref(ref)
+ , m_offset(0)
+ , m_length(ref->length())
+ {}
+
+ ByteArrayRef(const QByteArray *ref, int offset, int length)
+ : m_ref(ref)
+ , m_offset(offset)
+ , m_length(length)
+ {
+ Q_ASSERT(ref);
+ Q_ASSERT(offset >= 0);
+ Q_ASSERT(length >= 0);
+ Q_ASSERT(offset + length <= ref->size());
+ }
+
+ inline const char *start() const
+ { return m_ref ? m_ref->constData() + m_offset : 0; }
+
+ inline int length() const
+ { return m_length; }
+
+ inline int size() const
+ { return length(); }
+
+ inline char at(int pos) const
+ { return m_ref && pos >= 0 && pos < m_length ? m_ref->at(m_offset + pos) : '\0'; }
+
+ inline char operator[](int pos) const
+ { return at(pos); }
+
+ QByteArray toByteArray() const
+ { return m_ref ? QByteArray(m_ref->constData() + m_offset, m_length) : QByteArray(); }
+
+ bool operator==(const QByteArray &other) const
+ { return m_ref ? (m_length == other.length() && !qstrncmp(m_ref->constData() + m_offset, other.constData(), m_length)) : false; }
+ bool operator!=(const QByteArray &other) const
+ { return !this->operator==(other); }
+
+ bool startsWith(const char *ch) const;
+
+ int count(char c) const;
+
+private:
+ const QByteArray *m_ref;
+ int m_offset;
+ int m_length;
+};
+
+inline bool operator==(const QByteArray &other, const ByteArrayRef &ref)
+{ return ref == other; }
+
+inline bool operator!=(const QByteArray &other, const ByteArrayRef &ref)
+{ return ref != other; }
+
+class CPLUSPLUS_EXPORT PPToken: public Token
+{
+public:
+ PPToken();
+
+ PPToken(const QByteArray &src)
+ : m_src(src)
+ {}
+
+ void setSource(const QByteArray &src)
+ { m_src = src; }
+
+ const QByteArray &source() const
+ { return m_src; }
+
+ const char *start() const
+ { return m_src.constData() + offset; }
+
+ ByteArrayRef asByteArrayRef() const
+ { return ByteArrayRef(&m_src, offset, length()); }
+
+ bool isValid() const
+ { return !m_src.isEmpty(); }
+
+ void squeeze();
+
+private:
+ QByteArray m_src;
+};
+
+} // namespace Internal
+} // namespace CPlusPlus
+
+#endif // CPLUSPLUS_INTERNAL_PPTOKEN_H
diff --git a/src/libs/cplusplus/PreprocessorClient.h b/src/libs/cplusplus/PreprocessorClient.h
index 0056a6d869..c3c1dd9462 100644
--- a/src/libs/cplusplus/PreprocessorClient.h
+++ b/src/libs/cplusplus/PreprocessorClient.h
@@ -75,7 +75,7 @@ public:
public:
Client();
- virtual ~Client();
+ virtual ~Client() = 0;
virtual void macroAdded(const Macro &macro) = 0;
@@ -85,13 +85,13 @@ public:
virtual void startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
- bool inCondition = false,
const QVector<MacroArgumentReference> &actuals
= QVector<MacroArgumentReference>()) = 0;
virtual void stopExpandingMacro(unsigned offset,
const Macro &macro) = 0;
+ /// Start skipping from the given offset.
virtual void startSkippingBlocks(unsigned offset) = 0;
virtual void stopSkippingBlocks(unsigned offset) = 0;
diff --git a/src/libs/cplusplus/PreprocessorEnvironment.cpp b/src/libs/cplusplus/PreprocessorEnvironment.cpp
index 42c3bc04f4..174afe66a7 100644
--- a/src/libs/cplusplus/PreprocessorEnvironment.cpp
+++ b/src/libs/cplusplus/PreprocessorEnvironment.cpp
@@ -150,7 +150,7 @@ void Environment::reset()
_hash_count = 401;
}
-bool Environment::isBuiltinMacro(const QByteArray &s)
+bool Environment::isBuiltinMacro(const Internal::ByteArrayRef &s)
{
if (s.length() != 8)
return false;
@@ -236,6 +236,22 @@ Macro *Environment::resolve(const QByteArray &name) const
return it;
}
+Macro *Environment::resolve(const Internal::ByteArrayRef &name) const
+{
+ if (! _macros)
+ return 0;
+
+ Macro *it = _hash[hashCode(name) % _hash_count];
+ for (; it; it = it->_next) {
+ if (it->name() != name)
+ continue;
+ else if (it->isHidden())
+ return 0;
+ else break;
+ }
+ return it;
+}
+
unsigned Environment::hashCode(const QByteArray &s)
{
unsigned hash_value = 0;
@@ -246,6 +262,16 @@ unsigned Environment::hashCode(const QByteArray &s)
return hash_value;
}
+unsigned Environment::hashCode(const Internal::ByteArrayRef &s)
+{
+ unsigned hash_value = 0;
+
+ for (int i = 0; i < s.length(); ++i)
+ hash_value = (hash_value << 5) - hash_value + s.at(i);
+
+ return hash_value;
+}
+
void Environment::rehash()
{
if (_hash) {
diff --git a/src/libs/cplusplus/PreprocessorEnvironment.h b/src/libs/cplusplus/PreprocessorEnvironment.h
index a904f9c8fc..b03261fa89 100644
--- a/src/libs/cplusplus/PreprocessorEnvironment.h
+++ b/src/libs/cplusplus/PreprocessorEnvironment.h
@@ -53,6 +53,7 @@
#define CPLUSPLUS_PP_ENVIRONMENT_H
#include "CPlusPlusForwardDeclarations.h"
+#include "PPToken.h"
#include <QList>
#include <QByteArray>
@@ -78,6 +79,7 @@ public:
Macro *remove(const QByteArray &name);
Macro *resolve(const QByteArray &name) const;
+ Macro *resolve(const Internal::ByteArrayRef &name) const;
iterator firstMacro() const;
iterator lastMacro() const;
@@ -85,10 +87,11 @@ public:
void reset();
void addMacros(const QList<Macro> &macros);
- static bool isBuiltinMacro(const QByteArray &name);
+ static bool isBuiltinMacro(const Internal::ByteArrayRef &name);
private:
static unsigned hashCode(const QByteArray &s);
+ static unsigned hashCode(const Internal::ByteArrayRef &s);
void rehash();
public:
diff --git a/src/libs/cplusplus/cplusplus-lib.pri b/src/libs/cplusplus/cplusplus-lib.pri
index 57b2e2ffdf..677dff2556 100644
--- a/src/libs/cplusplus/cplusplus-lib.pri
+++ b/src/libs/cplusplus/cplusplus-lib.pri
@@ -51,11 +51,9 @@ HEADERS += \
$$PWD/pp.h \
$$PWD/pp-cctype.h \
$$PWD/pp-engine.h \
- $$PWD/pp-macro-expander.h \
$$PWD/pp-scanner.h \
- $$PWD/ModelManagerInterface.h \
$$PWD/findcdbbreakpoint.h \
- $$PWD/TypeHierarchyBuilder.h
+ $$PWD/PPToken.h
SOURCES += \
$$PWD/SimpleLexer.cpp \
@@ -80,10 +78,8 @@ SOURCES += \
$$PWD/FastPreprocessor.cpp \
$$PWD/Macro.cpp \
$$PWD/pp-engine.cpp \
- $$PWD/pp-macro-expander.cpp \
$$PWD/pp-scanner.cpp \
- $$PWD/ModelManagerInterface.cpp \
$$PWD/findcdbbreakpoint.cpp \
- $$PWD/TypeHierarchyBuilder.cpp
+ $$PWD/PPToken.cpp
RESOURCES += $$PWD/cplusplus.qrc
diff --git a/src/libs/cplusplus/cplusplus.qbs b/src/libs/cplusplus/cplusplus.qbs
index a1dbf42748..44aef810c2 100644
--- a/src/libs/cplusplus/cplusplus.qbs
+++ b/src/libs/cplusplus/cplusplus.qbs
@@ -122,8 +122,6 @@ QtcLibrary {
"Macro.h",
"MatchingText.cpp",
"MatchingText.h",
- "ModelManagerInterface.cpp",
- "ModelManagerInterface.h",
"NamePrettyPrinter.cpp",
"NamePrettyPrinter.h",
"Overview.cpp",
@@ -142,8 +140,6 @@ QtcLibrary {
"SnapshotSymbolVisitor.h",
"SymbolNameVisitor.cpp",
"SymbolNameVisitor.h",
- "TypeHierarchyBuilder.cpp",
- "TypeHierarchyBuilder.h",
"TypeOfExpression.cpp",
"TypeOfExpression.h",
"TypePrettyPrinter.cpp",
@@ -153,8 +149,8 @@ QtcLibrary {
"pp-cctype.h",
"pp-engine.cpp",
"pp-engine.h",
- "pp-macro-expander.cpp",
- "pp-macro-expander.h",
+ "PPToken.cpp",
+ "PPToken.h",
"pp-scanner.cpp",
"pp-scanner.h",
"pp.h",
diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp
index af27609f84..d92edb2115 100644
--- a/src/libs/cplusplus/pp-engine.cpp
+++ b/src/libs/cplusplus/pp-engine.cpp
@@ -52,6 +52,7 @@
#include "pp.h"
#include "pp-cctype.h"
+#include <Control.h>
#include <Lexer.h>
#include <Token.h>
#include <Literals.h>
@@ -60,9 +61,71 @@
#include <QtDebug>
#include <algorithm>
#include <QList>
+#include <QDate>
+#include <QTime>
+
+#define NO_DEBUG
+
+#ifndef NO_DEBUG
+# include <iostream>
+#endif // NO_DEBUG
+
+namespace {
+enum {
+ eagerExpansion = 1,
+ MAX_TOKEN_EXPANSION_COUNT = 5000,
+ MAX_TOKEN_BUFFER_DEPTH = 16000, // for when macros are using some kind of right-folding, this is the list of "delayed" buffers waiting to be expanded after the current one.
+};
+}
+
+namespace {
+template<typename _T>
+class ScopedSwap
+{
+ _T oldValue;
+ _T &ref;
+
+public:
+ ScopedSwap(_T &var, _T newValue)
+ : oldValue(newValue)
+ , ref(var)
+ {
+ std::swap(ref, oldValue);
+ }
+
+ ~ScopedSwap()
+ {
+ std::swap(ref, oldValue);
+ }
+};
+typedef ScopedSwap<bool> ScopedBoolSwap;
+typedef ScopedSwap<unsigned> ScopedUnsignedSwap;
+} // anonymous namespace
namespace CPlusPlus {
+namespace Internal {
+struct TokenBuffer
+{
+ std::list<PPToken> tokens;
+ const Macro *macro;
+ TokenBuffer *next;
+
+ TokenBuffer(const PPToken *start, const PPToken *end, const Macro *macro, TokenBuffer *next)
+ : tokens(start, end), macro(macro), next(next)
+ {}
+
+ bool isBlocked(const Macro *macro) const {
+ if (!macro)
+ return false;
+
+ for (const TokenBuffer *it = this; it; it = it->next)
+ if (it->macro == macro && it->macro->name() == macro->name())
+ return true;
+ return false;
+ }
+};
+
struct Value
{
enum Kind {
@@ -133,15 +196,20 @@ struct Value
#undef PP_DEFINE_BIN_OP
};
+} // namespace Internal
} // namespace CPlusPlus
-
using namespace CPlusPlus;
-
+using namespace CPlusPlus::Internal;
namespace {
-Macro *macroDefinition(QByteArray name, unsigned offset, Environment *env, Client *client)
+inline bool isValidToken(const PPToken &tk)
+{
+ return tk.isNot(T_EOF_SYMBOL) && (! tk.newline() || tk.joined());
+}
+
+Macro *macroDefinition(const QByteArray &name, unsigned offset, Environment *env, Client *client)
{
Macro *m = env->resolve(name);
if (client) {
@@ -452,763 +520,727 @@ private:
} // end of anonymous namespace
+Preprocessor::State::State()
+ : m_lexer(0)
+ , m_skipping(MAX_LEVEL)
+ , m_trueTest(MAX_LEVEL)
+ , m_ifLevel(0)
+ , m_tokenBuffer(0)
+ , m_tokenBufferDepth(0)
+ , m_inPreprocessorDirective(false)
+ , m_result(0)
+ , m_markGeneratedTokens(true)
+ , m_noLines(false)
+ , m_inCondition(false)
+ , m_inDefine(false)
+{
+ m_skipping[m_ifLevel] = false;
+ m_trueTest[m_ifLevel] = false;
+}
-Preprocessor::Preprocessor(Client *client, Environment *env)
- : client(client),
- env(env),
- _expand(env),
- _skipping(MAX_LEVEL),
- _trueTest(MAX_LEVEL),
- _dot(_tokens.end()),
- _result(0),
- _markGeneratedTokens(false),
- _expandMacros(true),
- _keepComments(false)
+void Preprocessor::State::pushTokenBuffer(const PPToken *start, const PPToken *end, const Macro *macro)
+{
+ if (m_tokenBufferDepth <= MAX_TOKEN_BUFFER_DEPTH) {
+#ifdef COMPRESS_TOKEN_BUFFER
+ // This does not work correctly for boost's preprocessor library, or that library exposes a bug in the code.
+ if (macro || !m_tokenBuffer) {
+ m_tokenBuffer = new TokenBuffer(start, end, macro, m_tokenBuffer);
+ ++m_tokenBufferDepth;
+ } else {
+ m_tokenBuffer->tokens.insert(m_tokenBuffer->tokens.begin(), start, end);
+ }
+// qDebug()<<"New depth:" << m_tokenBufferDepth << "with buffer size:" << m_tokenBuffer->tokens.size();
+#else
+ m_tokenBuffer = new TokenBuffer(start, end, macro, m_tokenBuffer);
+ ++m_tokenBufferDepth;
+#endif
+ } else {
+ //### Should we tell the user that his source is insane?
+// qDebug() << "Macro insanity level reached in" << m_currentFileName;
+ }
+}
+
+void Preprocessor::State::popTokenBuffer()
{
- resetIfLevel ();
+ TokenBuffer *r = m_tokenBuffer;
+ m_tokenBuffer = m_tokenBuffer->next;
+ delete r;
+
+ if (m_tokenBufferDepth)
+ --m_tokenBufferDepth;
}
-void Preprocessor::pushState(const State &s)
+Preprocessor::Preprocessor(Client *client, Environment *env)
+ : m_client(client)
+ , m_env(env)
+ , m_expandMacros(true)
+ , m_keepComments(false)
{
- _savedStates.append(state());
- _source = s.source;
- _tokens = s.tokens;
- _dot = s.dot;
}
-Preprocessor::State Preprocessor::state() const
+void Preprocessor::pushState(const State &newState)
{
- State state;
- state.source = _source;
- state.tokens = _tokens;
- state.dot = _dot;
- return state;
+ m_savedStates.append(m_state);
+ m_state = newState;
}
void Preprocessor::popState()
{
- const State &state = _savedStates.last();
- _source = state.source;
- _tokens = state.tokens;
- _dot = state.dot;
- _savedStates.removeLast();
+ const State &s = m_savedStates.last();
+ delete m_state.m_lexer;
+ m_state = s;
+ m_savedStates.removeLast();
}
QByteArray Preprocessor::operator()(const QString &fileName, const QString &source)
{
- const QString previousOriginalSource = _originalSource;
- _originalSource = source;
+ const QString previousOriginalSource = m_originalSource;
+ m_originalSource = source;
const QByteArray bytes = source.toLatin1();
const QByteArray preprocessedCode = operator()(fileName, bytes);
- _originalSource = previousOriginalSource;
+ m_originalSource = previousOriginalSource;
return preprocessedCode;
}
QByteArray Preprocessor::operator()(const QString &fileName,
- const QByteArray &source)
+ const QByteArray &source,
+ bool noLines,
+ bool markGeneratedTokens)
{
QByteArray preprocessed;
- preprocess(fileName, source, &preprocessed);
+// qDebug()<<"running" << fileName<<"with"<<source.count('\n')<<"lines...";
+ preprocess(fileName, source, &preprocessed, noLines, markGeneratedTokens, false);
return preprocessed;
}
-QByteArray Preprocessor::expand(const QByteArray &source)
-{
- QByteArray result;
- result.reserve(256);
- expand(source, &result);
- return result;
-}
-
-void Preprocessor::expand(const QByteArray &source, QByteArray *result)
-{
- if (result)
- _expand(source, result);
-}
-
-void Preprocessor::expand(const char *first, const char *last, QByteArray *result)
-{
- const QByteArray source = QByteArray::fromRawData(first, last - first);
- return expand(source, result);
-}
-
-void Preprocessor::out(const QByteArray &text)
+bool Preprocessor::expandMacros() const
{
- if (_result)
- _result->append(text);
+ return m_expandMacros;
}
-void Preprocessor::out(char ch)
+void Preprocessor::setExpandMacros(bool expandMacros)
{
- if (_result)
- _result->append(ch);
+ m_expandMacros = expandMacros;
}
-void Preprocessor::out(const char *s)
+bool Preprocessor::keepComments() const
{
- if (_result)
- _result->append(s);
+ return m_keepComments;
}
-bool Preprocessor::expandMacros() const
+void Preprocessor::setKeepComments(bool keepComments)
{
- return _expandMacros;
+ m_keepComments = keepComments;
}
-void Preprocessor::setExpandMacros(bool expandMacros)
+Preprocessor::State Preprocessor::createStateFromSource(const QString &fileName,
+ const QByteArray &source,
+ QByteArray *result,
+ bool noLines,
+ bool markGeneratedTokens,
+ bool inCondition) const
{
- _expandMacros = expandMacros;
+ State state;
+ state.m_currentFileName = fileName;
+ state.m_source = source;
+ state.m_lexer = new Lexer(source.constBegin(), source.constEnd());
+ state.m_lexer->setScanKeywords(false);
+ state.m_lexer->setScanAngleStringLiteralTokens(false);
+ if (m_keepComments)
+ state.m_lexer->setScanCommentTokens(true);
+ state.m_result = result;
+ state.m_noLines = noLines;
+ state.m_markGeneratedTokens = markGeneratedTokens;
+ state.m_inCondition = inCondition;
+ return state;
}
-bool Preprocessor::keepComments() const
+void Preprocessor::genLine(unsigned lineno, const QByteArray &fileName) const
{
- return _keepComments;
+ startNewOutputLine();
+ out("# ");
+ out(QByteArray::number(lineno));
+ out(" \"");
+ out(fileName);
+ out("\"\n");
}
-void Preprocessor::setKeepComments(bool keepComments)
+void Preprocessor::handleDefined(PPToken *tk)
{
- _keepComments = keepComments;
+ unsigned lineno = tk->lineno;
+ lex(tk); // consume "defined" token
+ bool lparenSeen = tk->is(T_LPAREN);
+ if (lparenSeen)
+ lex(tk); // consume "(" token
+ if (tk->isNot(T_IDENTIFIER))
+ //### TODO: generate error message
+ return;
+ PPToken idToken = *tk;
+ do {
+ lex(tk);
+ if (tk->isNot(T_POUND_POUND))
+ break;
+ lex(tk);
+ if (tk->is(T_IDENTIFIER))
+ idToken = generateConcatenated(idToken, *tk);
+ else
+ break;
+ } while (isValidToken(*tk));
+ pushToken(tk);
+ QByteArray result(1, '0');
+ if (m_env->resolve(idToken.asByteArrayRef()))
+ result[0] = '1';
+ *tk = generateToken(T_NUMERIC_LITERAL, ByteArrayRef(&result), lineno, false);
}
-Preprocessor::State Preprocessor::createStateFromSource(const QByteArray &source) const
+void Preprocessor::pushToken(Preprocessor::PPToken *tk)
{
- State state;
- state.source = source;
- Lexer lex(state.source.constBegin(), state.source.constEnd());
- lex.setScanKeywords(false);
- if (_keepComments)
- lex.setScanCommentTokens(true);
- Token tok;
- do {
- lex(&tok);
- state.tokens.append(tok);
- } while (tok.isNot(T_EOF_SYMBOL));
- state.dot = state.tokens.constBegin();
- return state;
+ const PPToken currentTokenBuffer[] = { *tk };
+ m_state.pushTokenBuffer(currentTokenBuffer, currentTokenBuffer + 1, 0);
}
-void Preprocessor::processNewline(bool force)
+void Preprocessor::lex(PPToken *tk)
{
- if (_dot != _tokens.constBegin()) {
- TokenIterator prevTok = _dot - 1;
-
- // Line changes due to multi-line tokens that we assume got printed
- // to the preprocessed source. See interaction with skipToNextLine.
- if (maybeMultilineToken(prevTok)) {
- const char *ptr = _source.constBegin() + prevTok->begin();
- const char *end = ptr + prevTok->length();
-
- for (; ptr != end; ++ptr) {
- if (*ptr == '\n')
- ++env->currentLine;
- }
+_Lagain:
+ if (m_state.m_tokenBuffer) {
+ if (m_state.m_tokenBuffer->tokens.empty()) {
+ m_state.popTokenBuffer();
+ goto _Lagain;
}
- }
-
- if (! force && env->currentLine == _dot->lineno)
- return;
-
- if (force || env->currentLine > _dot->lineno) {
- out("\n# ");
- out(QByteArray::number(_dot->lineno));
- out(' ');
- out('"');
- out(env->currentFile.toUtf8());
- out('"');
- out('\n');
+ *tk = m_state.m_tokenBuffer->tokens.front();
+ m_state.m_tokenBuffer->tokens.pop_front();
} else {
- for (unsigned i = env->currentLine; i < _dot->lineno; ++i)
- out('\n');
+ tk->setSource(m_state.m_source);
+ m_state.m_lexer->scan(tk);
}
- env->currentLine = _dot->lineno;
+// if (tk->isValid() && !tk->generated() && !tk->is(T_EOF_SYMBOL))
+// m_env->currentLine = tk->lineno;
+
+_Lclassify:
+ if (! m_state.m_inPreprocessorDirective) {
+ if (tk->newline() && tk->is(T_POUND)) {
+ handlePreprocessorDirective(tk);
+ goto _Lclassify;
+ } else if (tk->newline() && skipping()) {
+ ScopedBoolSwap s(m_state.m_inPreprocessorDirective, true);
+ do {
+ lex(tk);
+ } while (isValidToken(*tk));
+ goto _Lclassify;
+ } else if (tk->is(T_IDENTIFIER) && !isQtReservedWord(tk->asByteArrayRef())) {
+ static const QByteArray ppDefined("defined");
+ if (m_state.m_inCondition && tk->asByteArrayRef() == ppDefined)
+ handleDefined(tk);
+ else if (handleIdentifier(tk))
+ goto _Lagain;
+ }
+ }
}
-void Preprocessor::processSkippingBlocks(bool skippingBlocks,
- TokenIterator start, TokenIterator /*end*/)
+void Preprocessor::skipPreprocesorDirective(PPToken *tk)
{
- if (! client)
- return;
-
- if (skippingBlocks != _skipping[iflevel]) {
- unsigned offset = start->offset;
+ ScopedBoolSwap s(m_state.m_inPreprocessorDirective, true);
- if (_skipping[iflevel]) {
- if (_dot->f.newline)
- ++offset;
-
- client->startSkippingBlocks(offset);
-
- } else {
- if (offset)
- --offset;
-
- client->stopSkippingBlocks(offset);
- }
+ while (isValidToken(*tk)) {
+ lex(tk);
}
}
-bool Preprocessor::markGeneratedTokens(bool markGeneratedTokens,
- TokenIterator dot)
+bool Preprocessor::handleIdentifier(PPToken *tk)
{
- bool previous = _markGeneratedTokens;
- _markGeneratedTokens = markGeneratedTokens;
-
- if (previous != _markGeneratedTokens) {
- if (! dot)
- dot = _dot;
-
- if (_markGeneratedTokens)
- out("\n#gen true");
- else
- out("\n#gen false");
-
- processNewline(/*force = */ true);
-
- const char *begin = _source.constBegin();
- const char *end = begin;
-
- if (markGeneratedTokens)
- end += dot->begin();
- else
- end += (dot - 1)->end();
-
- const char *it = end - 1;
- for (; it != begin - 1; --it) {
- if (*it == '\n')
- break;
+ ScopedBoolSwap s(m_state.m_inPreprocessorDirective, true);
+
+ static const QByteArray ppLine("__LINE__");
+ static const QByteArray ppFile("__FILE__");
+ static const QByteArray ppDate("__DATE__");
+ static const QByteArray ppTime("__TIME__");
+
+ ByteArrayRef macroNameRef = tk->asByteArrayRef();
+ bool newline = tk->newline();
+
+ if (!m_state.m_inDefine && macroNameRef.size() == 8 && macroNameRef[0] == '_' && macroNameRef[1] == '_') {
+ PPToken newTk;
+ if (macroNameRef == ppLine) {
+ QByteArray txt = QByteArray::number(tk->lineno);
+ newTk = generateToken(T_STRING_LITERAL, &txt, tk->lineno, false);
+ } else if (macroNameRef == ppFile) {
+ QByteArray txt;
+ txt.append('"');
+ txt.append(m_env->currentFile.toUtf8());
+ txt.append('"');
+ newTk = generateToken(T_STRING_LITERAL, &txt, tk->lineno, false);
+ } else if (macroNameRef == ppDate) {
+ QByteArray txt;
+ txt.append('"');
+ txt.append(QDate::currentDate().toString().toUtf8());
+ txt.append('"');
+ newTk = generateToken(T_STRING_LITERAL, &txt, tk->lineno, false);
+ } else if (macroNameRef == ppTime) {
+ QByteArray txt;
+ txt.append('"');
+ txt.append(QTime::currentTime().toString().toUtf8());
+ txt.append('"');
+ newTk = generateToken(T_STRING_LITERAL, &txt, tk->lineno, false);
}
- ++it;
- for (; it != end; ++it) {
- if (! pp_isspace(*it))
- out(' ');
-
- else
- out(*it);
+ if (newTk.isValid()) {
+ newTk.f.newline = newline;
+ newTk.f.whitespace = tk->whitespace();
+ *tk = newTk;
+ return false;
}
-
- if (! markGeneratedTokens && dot->f.newline)
- processNewline(/*force = */ true);
}
- return previous;
-}
+ const QByteArray macroName = macroNameRef.toByteArray();
+ Macro *macro = m_env->resolve(macroName);
+ if (!macro)
+ return false;
+ if (tk->generated() && m_state.m_tokenBuffer && m_state.m_tokenBuffer->isBlocked(macro))
+ return false;
+// qDebug() << "expanding" << macro->name() << "on line" << tk->lineno;
+
+ if (m_client && !tk->generated())
+ m_client->startExpandingMacro(tk->offset, *macro, macroName);
+ QVector<PPToken> body = macro->definitionTokens();
+
+ if (macro->isFunctionLike()) {
+ if (!expandMacros() || !handleFunctionLikeMacro(tk, macro, body, !m_state.m_inDefine)) {
+ // the call is not function like or expandMacros() returns false, so stop
+ if (m_client && !tk->generated())
+ m_client->stopExpandingMacro(tk->offset, *macro);
+ return false;
+ }
-bool Preprocessor::maybeAfterComment() const
-{
- unsigned endOfPreviousToken = 0;
+ }
- if (_dot != _tokens.constBegin())
- endOfPreviousToken = (_dot - 1)->end();
+ if (body.isEmpty()) {
+ if (!m_state.m_inDefine) {
+ // macro expanded to empty, so characters disappeared, hence force a re-indent.
+ PPToken forceWhitespacingToken;
+ // special case: for a macro that expanded to empty, we do not want
+ // to generate a new #line and re-indent, but just generate the
+ // amount of spaces that the macro name took up.
+ forceWhitespacingToken.f.length = tk->length() + (tk->whitespace() ? 1 : 0);
+ body.push_front(forceWhitespacingToken);
+ }
+ } else {
+ PPToken &firstNewTk = body.first();
+ firstNewTk.f.newline = newline;
+ firstNewTk.f.whitespace = true; // the macro call is removed, so space the first token correctly.
+ }
- const char *start = _source.constBegin() + endOfPreviousToken;
+ m_state.pushTokenBuffer(body.begin(), body.end(), macro);
- if (*start == '/')
- return true;
+ if (m_client && !tk->generated())
+ m_client->stopExpandingMacro(tk->offset, *macro);
- return false;
+ return true;
}
-bool Preprocessor::maybeMultilineToken(Preprocessor::TokenIterator tok)
+bool Preprocessor::handleFunctionLikeMacro(PPToken *tk, const Macro *macro, QVector<PPToken> &body, bool addWhitespaceMarker)
{
- return tok->isLiteral()
- || (_keepComments
- && (tok->kind() == T_COMMENT
- || tok->kind() == T_DOXY_COMMENT));
-}
+ static const QByteArray ppVaArgs("__VA_ARGS__");
+
+ QVector<QVector<PPToken> > actuals;
+ PPToken idToken = *tk;
+ if (!collectActualArguments(tk, &actuals)) {
+ pushToken(tk);
+ *tk = idToken;
+ return false;
+ }
-void Preprocessor::skipToNextLine()
-{
- do {
- if (maybeMultilineToken(_dot)) {
- const char *ptr = _source.constBegin() + _dot->begin();
- const char *end = ptr + _dot->length();
-
- int newlines = 0;
- for (; ptr != end; ++ptr) {
- if (*ptr == '\n')
- ++newlines;
- }
- if (newlines) {
- // This function does not output tokens it skips. We need to offset
- // the currentLine so it gets correctly adjusted by processNewline.
- env->currentLine -= newlines;
- ++_dot;
- return;
+ QVector<PPToken> expanded;
+ for (size_t i = 0, bodySize = body.size(); i < bodySize && expanded.size() < MAX_TOKEN_EXPANSION_COUNT; ++i) {
+ int expandedSize = expanded.size();
+ const PPToken &token = body[i];
+
+ if (token.is(T_IDENTIFIER)) {
+ const ByteArrayRef id = token.asByteArrayRef();
+ const QVector<QByteArray> &formals = macro->formals();
+ int j = 0;
+ for (; j < formals.size() && expanded.size() < MAX_TOKEN_EXPANSION_COUNT; ++j) {
+ if (formals[j] == id) {
+ if (actuals.size() <= j) {
+ // too few actual parameters
+ //### TODO: error message
+ goto exitNicely;
+ }
+
+ QVector<PPToken> actualsForThisParam = actuals[j];
+ if (id == ppVaArgs || (macro->isVariadic() && j + 1 == formals.size())) {
+ unsigned lineno = 0;
+ QByteArray comma(",");
+ ByteArrayRef commaRef(&comma);
+ for (int k = j + 1; k < actuals.size(); ++k) {
+ if (!actualsForThisParam.isEmpty())
+ lineno = actualsForThisParam.last().lineno;
+ actualsForThisParam.append(generateToken(T_COMMA, commaRef, lineno, true));
+ actualsForThisParam += actuals[k];
+ }
+ }
+
+ if (i > 0 && body[i - 1].is(T_POUND)) {
+ QByteArray newText;
+ newText.reserve(256);
+ unsigned lineno = 0;
+ for (int i = 0, ei = actualsForThisParam.size(); i < ei; ++i) {
+ const PPToken &t = actualsForThisParam.at(i);
+ if (i == 0)
+ lineno = t.lineno;
+ else if (t.whitespace())
+ newText.append(' ');
+ newText.append(t.start(), t.length());
+ }
+ newText.replace("\\", "\\\\");
+ newText.replace("\"", "\\\"");
+ expanded.push_back(generateToken(T_STRING_LITERAL, ByteArrayRef(&newText), lineno, true));
+ } else {
+ expanded += actualsForThisParam;
+ }
+ break;
+ }
}
+
+ if (j == formals.size())
+ expanded.push_back(token);
+ } else if (token.isNot(T_POUND) && token.isNot(T_POUND_POUND)) {
+ expanded.push_back(token);
}
- ++_dot;
+ if (i > 1 && body[i - 1].is(T_POUND_POUND)) {
+ if (expandedSize < 1 || expanded.size() == expandedSize) //### TODO: [cpp.concat] placemarkers
+ continue;
+ const PPToken &leftTk = expanded[expandedSize - 1];
+ const PPToken &rightTk = expanded[expandedSize];
+ expanded[expandedSize - 1] = generateConcatenated(leftTk, rightTk);
+ expanded.remove(expandedSize);
+ }
+ }
- } while (_dot->isNot(T_EOF_SYMBOL)
- && (_dot->f.joined || !_dot->f.newline));
+exitNicely:
+ pushToken(tk);
+ if (addWhitespaceMarker) {
+ PPToken forceWhitespacingToken;
+ expanded.push_front(forceWhitespacingToken);
+ }
+ body = expanded;
+ return true;
}
+/// invalid pp-tokens are used as markers to force whitespace checks.
void Preprocessor::preprocess(const QString &fileName, const QByteArray &source,
- QByteArray *result)
+ QByteArray *result, bool noLines,
+ bool markGeneratedTokens, bool inCondition)
{
- const int previousIfLevel = iflevel;
-
- QByteArray *previousResult = _result;
- _result = result;
-
- pushState(createStateFromSource(source));
-
- const QString previousFileName = env->currentFile;
- env->currentFile = fileName;
-
- const unsigned previousCurrentLine = env->currentLine;
- env->currentLine = 0;
-
- while (true) {
-
- if (_dot->f.joined)
- out("\\");
+ if (source.isEmpty())
+ return;
- processNewline();
+ pushState(createStateFromSource(fileName, source, result, noLines, markGeneratedTokens, inCondition));
- if (_dot->is(T_EOF_SYMBOL)) {
- break;
+ const QString previousFileName = m_env->currentFile;
+ m_env->currentFile = fileName;
- } else if (_dot->is(T_POUND) && (! _dot->f.joined && _dot->f.newline)) {
- // handle the preprocessor directive
+ const unsigned previousCurrentLine = m_env->currentLine;
+ m_env->currentLine = 1;
- TokenIterator start = _dot;
- skipToNextLine();
+ const QByteArray fn = fileName.toUtf8();
+ if (!m_state.m_noLines)
+ genLine(1, fn);
- const bool skippingBlocks = _skipping[iflevel];
+ PPToken tk(m_state.m_source), prevTk;
+ do {
+_Lrestart:
+ bool forceLine = false;
+ lex(&tk);
- processDirective(start, _dot);
- processSkippingBlocks(skippingBlocks, start, _dot);
+ if (!tk.isValid()) {
+ bool wasGenerated = prevTk.generated();
+ prevTk = tk;
+ prevTk.f.generated = wasGenerated;
+ goto _Lrestart;
+ }
- } else if (skipping()) {
- // skip the current line
- skipToNextLine();
+ if (m_state.m_markGeneratedTokens && tk.generated() && !prevTk.generated()) {
+ startNewOutputLine();
+ out("#gen true\n");
+ ++m_env->currentLine;
+ forceLine = true;
+ } else if (m_state.m_markGeneratedTokens && !tk.generated() && prevTk.generated()) {
+ startNewOutputLine();
+ out("#gen false\n");
+ ++m_env->currentLine;
+ forceLine = true;
+ }
+ if (forceLine || m_env->currentLine != tk.lineno) {
+ if (forceLine || m_env->currentLine > tk.lineno || tk.lineno - m_env->currentLine > 3) {
+ if (m_state.m_noLines) {
+ if (!m_state.m_markGeneratedTokens)
+ out(' ');
+ } else {
+ genLine(tk.lineno, fn);
+ }
+ } else {
+ for (unsigned i = m_env->currentLine; i < tk.lineno; ++i)
+ out('\n');
+ }
} else {
+ if (tk.newline() && prevTk.isValid())
+ out('\n');
+ }
- if (_dot->f.whitespace || maybeAfterComment()) {
- unsigned endOfPreviousToken = 0;
-
- if (_dot != _tokens.constBegin())
- endOfPreviousToken = (_dot - 1)->end();
-
- const unsigned beginOfToken = _dot->begin();
-
- const char *start = _source.constBegin() + endOfPreviousToken;
- const char *end = _source.constBegin() + beginOfToken;
-
+ if (tk.whitespace() || prevTk.generated() != tk.generated() || !prevTk.isValid()) {
+ if (prevTk.generated() && tk.generated()) {
+ out(' ');
+ } else if (tk.isValid() && !prevTk.isValid() && tk.lineno == m_env->currentLine) {
+ out(QByteArray(prevTk.length() + (tk.whitespace() ? 1 : 0), ' '));
+ } else if (prevTk.generated() != tk.generated() || !prevTk.isValid()) {
+ const char *begin = tk.source().constBegin();
+ const char *end = begin + tk.offset;
const char *it = end - 1;
- for (; it != start - 1; --it) {
+ for (; it >= begin; --it)
if (*it == '\n')
break;
- }
++it;
-
- for (; it != end; ++it) {
- if (pp_isspace(*it))
- out(*it);
-
- else
- out(' ');
- }
- }
-
- if (_dot->isNot(T_IDENTIFIER)) {
- out(tokenSpell(*_dot));
- ++_dot;
-
+ for (; it < end; ++it)
+ out(' ');
} else {
- const TokenIterator identifierToken = _dot;
- ++_dot; // skip T_IDENTIFIER
-
- const QByteArray spell = tokenSpell(*identifierToken);
- if (! _expandMacros) {
- if (! env->isBuiltinMacro(spell)) {
- Macro *m = env->resolve(spell);
- if (m && ! m->isFunctionLike()) {
- // expand object-like macros.
- processObjectLikeMacro(identifierToken, spell, m);
- continue;
- }
- }
- out(spell);
- continue;
- }
-
- else if (env->isBuiltinMacro(spell))
- expandBuiltinMacro(identifierToken, spell);
-
- else {
- if (Macro *m = env->resolve(spell)) {
- if (! m->isFunctionLike()) {
- if (0 == (m = processObjectLikeMacro(identifierToken, spell, m)))
- continue;
-
- // the macro expansion generated something that looks like
- // a function-like macro.
- }
-
- // `m' is function-like macro.
- if (_dot->is(T_LPAREN)) {
- QVector<MacroArgumentReference> actuals;
- collectActualArguments(&actuals);
-
- if (_dot->is(T_RPAREN)) {
- expandFunctionLikeMacro(identifierToken, m, actuals);
- continue;
- }
- }
- }
-
- // it's not a function or object-like macro.
- out(spell);
- }
+ const char *begin = tk.source().constBegin();
+ const char *end = begin + tk.offset;
+ const char *it = end - 1;
+ for (; it >= begin; --it)
+ if (!pp_isspace(*it) || *it == '\n')
+ break;
+ ++it;
+ for (; it < end; ++it)
+ out(*it);
}
}
- }
- popState();
+ const ByteArrayRef tkBytes = tk.asByteArrayRef();
+ out(tkBytes);
+ m_env->currentLine = tk.lineno;
+ if (tk.is(T_COMMENT) || tk.is(T_DOXY_COMMENT))
+ m_env->currentLine += tkBytes.count('\n');
+ prevTk = tk;
+ } while (tk.isNot(T_EOF_SYMBOL));
- env->currentFile = previousFileName;
- env->currentLine = previousCurrentLine;
- _result = previousResult;
+ popState();
- iflevel = previousIfLevel;
+ m_env->currentFile = previousFileName;
+ m_env->currentLine = previousCurrentLine;
}
-void Preprocessor::collectActualArguments(QVector<MacroArgumentReference> *actuals)
+bool Preprocessor::collectActualArguments(PPToken *tk, QVector<QVector<PPToken> > *actuals)
{
- if (_dot->isNot(T_LPAREN))
- return;
-
- ++_dot;
-
- if (_dot->is(T_RPAREN))
- return;
-
- actuals->append(collectOneActualArgument());
+ Q_ASSERT(tk);
+ Q_ASSERT(actuals);
- while (_dot->is(T_COMMA)) {
- ++_dot;
+ lex(tk); // consume the identifier
- actuals->append(collectOneActualArgument());
- }
-}
+ if (tk->isNot(T_LPAREN))
+ //### TODO: error message
+ return false;
-MacroArgumentReference Preprocessor::collectOneActualArgument()
-{
- const unsigned position = _dot->begin();
+ QVector<PPToken> tokens;
+ lex(tk);
+ scanActualArgument(tk, &tokens);
- while (_dot->isNot(T_EOF_SYMBOL)) {
- if (_dot->is(T_COMMA) || _dot->is(T_RPAREN))
- break;
+ actuals->append(tokens);
- if (_dot->isNot(T_LPAREN))
- ++_dot;
+ while (tk->is(T_COMMA)) {
+ lex(tk);
- else {
- int count = 0;
-
- for (; _dot->isNot(T_EOF_SYMBOL); ++_dot) {
- if (_dot->is(T_LPAREN))
- ++count;
-
- else if (_dot->is(T_RPAREN)) {
- if (! --count) {
- ++_dot;
- break;
- }
- }
- }
- }
+ QVector<PPToken> tokens;
+ scanActualArgument(tk, &tokens);
+ actuals->append(tokens);
}
- const unsigned end = _dot->begin();
-
- return MacroArgumentReference(position, end - position);
+ if (tk->is(T_RPAREN))
+ lex(tk);
+ //###TODO: else error message
+ return true;
}
-Macro *Preprocessor::processObjectLikeMacro(TokenIterator identifierToken,
- const QByteArray &spell,
- Macro *m)
+void Preprocessor::scanActualArgument(PPToken *tk, QVector<PPToken> *tokens)
{
- QByteArray tmp;
- expandObjectLikeMacro(identifierToken, spell, m, &tmp);
-
- if (_dot->is(T_LPAREN)) {
- // check if the expension generated a function-like macro.
-
- m = 0; // reset the active the macro
-
- pushState(createStateFromSource(tmp));
+ Q_ASSERT(tokens);
- if (_dot->is(T_IDENTIFIER)) {
- const QByteArray id = tokenSpell(*_dot);
+ int count = 0;
- if (Macro *macro = env->resolve(id)) {
- if (macro->isFunctionLike())
- m = macro;
- }
+ while (tk->isNot(T_EOF_SYMBOL)) {
+ if (tk->is(T_LPAREN)) {
+ ++count;
+ } else if (tk->is(T_RPAREN)) {
+ if (! count)
+ break;
+ --count;
+ } else if (! count && tk->is(T_COMMA)) {
+ break;
}
- popState();
-
- if (m != 0)
- return m;
+ tokens->append(*tk);
+ lex(tk);
}
-
- const bool was = markGeneratedTokens(true, identifierToken);
- out(tmp);
- (void) markGeneratedTokens(was);
- return 0;
}
-void Preprocessor::expandBuiltinMacro(TokenIterator identifierToken,
- const QByteArray &spell)
+void Preprocessor::handlePreprocessorDirective(PPToken *tk)
{
- const bool was = markGeneratedTokens(true, identifierToken);
- expand(spell, _result);
- (void) markGeneratedTokens(was);
-}
+ ScopedBoolSwap s(m_state.m_inPreprocessorDirective, true);
-void Preprocessor::expandObjectLikeMacro(TokenIterator identifierToken,
- const QByteArray &spell,
- Macro *m,
- QByteArray *result)
-{
- if (client)
- client->startExpandingMacro(identifierToken->offset,
- *m, spell, false);
-
- m->setHidden(true);
- expand(m->definition(), result);
- m->setHidden(false);
+ PPToken poundToken = *tk;
+ lex(tk); // scan the directive
- if (client)
- client->stopExpandingMacro(_dot->offset, *m);
-}
-
-void Preprocessor::expandFunctionLikeMacro(TokenIterator identifierToken,
- Macro *m,
- const QVector<MacroArgumentReference> &actuals)
-{
- const char *beginOfText = startOfToken(*identifierToken);
- const char *endOfText = endOfToken(*_dot);
- ++_dot; // skip T_RPAREN
+ if (tk->newline() && ! tk->joined())
+ return; // nothing to do.
- if (client) {
- const QByteArray text =
- QByteArray::fromRawData(beginOfText,
- endOfText - beginOfText);
+ static const QByteArray ppDefine("define");
+ static const QByteArray ppIf("if");
+ static const QByteArray ppIfDef("ifdef");
+ static const QByteArray ppIfNDef("ifndef");
+ static const QByteArray ppEndIf("endif");
+ static const QByteArray ppElse("else");
+ static const QByteArray ppUndef("undef");
+ static const QByteArray ppElif("elif");
+ static const QByteArray ppInclude("include");
+ static const QByteArray ppIncludeNext("include_next");
+ static const QByteArray ppImport("import");
+ //### TODO:
+ // line
+ // error
+ // pragma
- client->startExpandingMacro(identifierToken->offset,
- *m, text, false, actuals);
+ if (tk->is(T_IDENTIFIER)) {
+ const ByteArrayRef directive = tk->asByteArrayRef();
+
+ if (!skipping() && directive == ppDefine)
+ handleDefineDirective(tk);
+ else if (!skipping() && directive == ppUndef)
+ handleUndefDirective(tk);
+ else if (!skipping() && (directive == ppInclude
+ || directive == ppIncludeNext
+ || directive == ppImport))
+ handleIncludeDirective(tk);
+ else if (directive == ppIf)
+ handleIfDirective(tk);
+ else if (directive == ppIfDef)
+ handleIfDefDirective(false, tk);
+ else if (directive == ppIfNDef)
+ handleIfDefDirective(true, tk);
+ else if (directive == ppEndIf)
+ handleEndIfDirective(tk, poundToken);
+ else if (directive == ppElse)
+ handleElseDirective(tk, poundToken);
+ else if (directive == ppElif)
+ handleElifDirective(tk, poundToken);
+
+ skipPreprocesorDirective(tk);
}
-
- const bool was = markGeneratedTokens(true, identifierToken);
- expand(beginOfText, endOfText, _result);
- (void) markGeneratedTokens(was);
-
- if (client)
- client->stopExpandingMacro(_dot->offset, *m);
}
-const char *Preprocessor::startOfToken(const Token &token) const
-{ return _source.constBegin() + token.begin(); }
-
-const char *Preprocessor::endOfToken(const Token &token) const
-{ return _source.constBegin() + token.end(); }
-QByteArray Preprocessor::tokenSpell(const Token &token) const
+void Preprocessor::handleIncludeDirective(PPToken *tk)
{
- const QByteArray text = QByteArray::fromRawData(_source.constBegin() + token.offset,
- token.f.length);
- return text;
-}
-
-QByteArray Preprocessor::tokenText(const Token &token) const
-{
- const QByteArray text(_source.constBegin() + token.offset,
- token.f.length);
- return text;
-}
-
-void Preprocessor::processDirective(TokenIterator firstToken, TokenIterator lastToken)
-{
- RangeLexer tk(firstToken, lastToken);
- ++tk; // skip T_POUND
-
- if (tk->is(T_IDENTIFIER)) {
- const QByteArray directive = tokenSpell(*tk);
- switch (PP_DIRECTIVE_TYPE d = classifyDirective(directive)) {
- case PP_DEFINE:
- if (! skipping())
- processDefine(firstToken, lastToken);
- break;
-
- case PP_INCLUDE:
- case PP_INCLUDE_NEXT:
- case PP_IMPORT:
- if (! skipping())
- processInclude(d == PP_INCLUDE_NEXT, firstToken, lastToken);
- break;
-
- case PP_UNDEF:
- if (! skipping())
- processUndef(firstToken, lastToken);
- break;
-
- case PP_ELIF:
- processElif(firstToken, lastToken);
- break;
-
- case PP_ELSE:
- processElse(firstToken, lastToken);
- break;
-
- case PP_ENDIF:
- processEndif(firstToken, lastToken);
- break;
-
- case PP_IF:
- processIf(firstToken, lastToken);
- break;
-
- case PP_IFDEF:
- case PP_IFNDEF:
- processIfdef(d == PP_IFNDEF, firstToken, lastToken);
- break;
+ m_state.m_lexer->setScanAngleStringLiteralTokens(true);
+ lex(tk); // consume "include" token
+ m_state.m_lexer->setScanAngleStringLiteralTokens(false);
+ const unsigned line = tk->lineno;
+ QByteArray included;
+
+ if (tk->is(T_STRING_LITERAL) || tk->is(T_ANGLE_STRING_LITERAL)) {
+ included = tk->asByteArrayRef().toByteArray();
+ lex(tk); // consume string token
+ } else {
+ included = expand(tk);
+ }
+ included = included.trimmed();
- default:
- break;
- } // switch
+ if (included.isEmpty()) {
+ //### TODO: error message
+ return;
}
-}
-QVector<Token> Preprocessor::tokenize(const QByteArray &text) const
-{
- QVector<Token> tokens;
- Lexer lex(text.constBegin(), text.constEnd());
- lex.setScanKeywords(false);
- Token tk;
- do {
- lex(&tk);
- tokens.append(tk);
- } while (tk.isNot(T_EOF_SYMBOL));
- return tokens;
+// qDebug("include [[%s]]", included.toUtf8().constData());
+ Client::IncludeType mode;
+ if (included.at(0) == '"')
+ mode = Client::IncludeLocal;
+ else if (included.at(0) == '<')
+ mode = Client::IncludeGlobal;
+ else
+ return; //### TODO: add error message?
+
+ included = included.mid(1, included.size() - 2);
+ QString inc = QString::fromUtf8(included.constData());
+ if (m_client)
+ m_client->sourceNeeded(inc, mode, line);
}
-void Preprocessor::processInclude(bool, TokenIterator firstToken,
- TokenIterator lastToken, bool acceptMacros)
+void Preprocessor::handleDefineDirective(PPToken *tk)
{
- if (! client)
- return; // nothing to do.
-
- RangeLexer tk(firstToken, lastToken);
- ++tk; // skip T_POUND
- ++tk; // skip `include|nclude_next'
-
- if (acceptMacros && tk->is(T_IDENTIFIER)) {
- // ### TODO: implement me
-#if 0
- QByteArray name;
- name.reserve(256);
- MacroExpander expandInclude(env);
- expandInclude(startOfToken(tokens.at(2)),
- startOfToken(tokens.last()),
- &name);
- const QByteArray previousSource = switchSource(name);
- //processInclude(skipCurentPath, tokenize(name), /*accept macros=*/ false);
- (void) switchSource(previousSource);
-#endif
-
- } else if (tk->is(T_LESS)) {
-
- TokenIterator start = tk.dot();
+ const unsigned defineOffset = tk->offset;
+ lex(tk); // consume "define" token
- for (; tk->isNot(T_EOF_SYMBOL); ++tk) {
- if (tk->is(T_GREATER))
- break;
- }
-
- const char *beginOfPath = endOfToken(*start);
- const char *endOfPath = startOfToken(*tk);
-
- QString fn = string(beginOfPath, endOfPath - beginOfPath);
- client->sourceNeeded(fn, Client::IncludeGlobal, firstToken->lineno);
-
- } else if (tk->is(T_ANGLE_STRING_LITERAL) || tk->is(T_STRING_LITERAL)) {
+ bool hasIdentifier = false;
+ if (tk->isNot(T_IDENTIFIER))
+ return;
- const QByteArray spell = tokenSpell(*tk);
- const char *beginOfPath = spell.constBegin();
- const char *endOfPath = spell.constEnd();
- const char quote = *beginOfPath;
+ ScopedBoolSwap inDefine(m_state.m_inDefine, true);
- if (beginOfPath + 1 != endOfPath && ((quote == '"' && endOfPath[-1] == '"') ||
- (quote == '<' && endOfPath[-1] == '>'))) {
+ Macro macro;
+ macro.setFileName(m_env->currentFile);
+ macro.setLine(tk->lineno);
+ QByteArray macroName = tk->asByteArrayRef().toByteArray();
+ macro.setName(macroName);
+ macro.setOffset(tk->offset);
- QString fn = string(beginOfPath + 1, spell.length() - 2);
- client->sourceNeeded(fn, Client::IncludeLocal, firstToken->lineno);
- }
- }
-}
+ lex(tk);
-void Preprocessor::processDefine(TokenIterator firstToken, TokenIterator lastToken)
-{
- RangeLexer tk(firstToken, lastToken);
+ if (isValidToken(*tk) && tk->is(T_LPAREN) && ! tk->whitespace()) {
+ macro.setFunctionLike(true);
- if (tk.size() < 3)
- return; // nothing to do
+ lex(tk); // skip `('
- ++tk; // skip T_POUND
- ++tk; // skip T_DEFINE
+ if (isValidToken(*tk) && tk->is(T_IDENTIFIER)) {
+ hasIdentifier = true;
+ macro.addFormal(tk->asByteArrayRef().toByteArray());
- if (tk->isNot(T_IDENTIFIER)) {
- // ### warning expected an `identifier'
- return;
- }
+ lex(tk);
- Macro macro;
- macro.setFileName(env->currentFile);
- macro.setLine(env->currentLine);
- macro.setName(tokenText(*tk));
- macro.setOffset(firstToken->offset);
- macro.setLength(endOfToken(lastToken[- 1]) - startOfToken(*firstToken));
- ++tk; // skip T_IDENTIFIER
-
- if (tk->is(T_LPAREN) && ! tk->f.whitespace) {
- // a function-like macro definition
- macro.setFunctionLike(true);
+ while (isValidToken(*tk) && tk->is(T_COMMA)) {
+ lex(tk);
- ++tk; // skip T_LPAREN
- if (tk->is(T_IDENTIFIER)) {
- macro.addFormal(tokenText(*tk));
- ++tk; // skip T_IDENTIFIER
- while (tk->is(T_COMMA)) {
- ++tk;// skip T_COMMA
- if (tk->isNot(T_IDENTIFIER))
- break;
- macro.addFormal(tokenText(*tk));
- ++tk; // skip T_IDENTIFIER
+ if (isValidToken(*tk) && tk->is(T_IDENTIFIER)) {
+ macro.addFormal(tk->asByteArrayRef().toByteArray());
+ lex(tk);
+ } else {
+ hasIdentifier = false;
+ }
}
}
if (tk->is(T_DOT_DOT_DOT)) {
macro.setVariadic(true);
- ++tk; // skip T_DOT_DOT_DOT
- }
-
- if (tk->isNot(T_RPAREN)) {
- // ### warning expected `)'
- return;
+ if (!hasIdentifier)
+ macro.addFormal("__VA_ARGS__");
+ lex(tk); // consume elipsis token
}
+ if (isValidToken(*tk) && tk->is(T_RPAREN))
+ lex(tk); // consume ")" token
+ }
- ++tk; // skip T_RPAREN
+ QVector<PPToken> bodyTokens;
+ PPToken firstBodyToken = *tk;
+ while (isValidToken(*tk)) {
+ tk->f.generated = true;
+ bodyTokens.push_back(*tk);
+ lex(tk);
+ if (eagerExpansion)
+ while (tk->is(T_IDENTIFIER) && !isQtReservedWord(tk->asByteArrayRef()) && handleIdentifier(tk))
+ lex(tk);
}
- if (isQtReservedWord(macro.name())) {
+ if (isQtReservedWord(ByteArrayRef(&macroName))) {
QByteArray macroId = macro.name();
if (macro.isFunctionLike()) {
@@ -1223,265 +1255,232 @@ void Preprocessor::processDefine(TokenIterator firstToken, TokenIterator lastTok
macroId += ')';
}
- macro.setDefinition(macroId);
+ bodyTokens.clear();
+ macro.setDefinition(macroId, bodyTokens);
} else {
- // ### make me fast!
- const char *startOfDefinition = startOfToken(*tk);
- const char *endOfDefinition = endOfToken(lastToken[- 1]);
- // It could be that the start is not really before that end, so the check...
- if (startOfDefinition < endOfDefinition) {
- QList<unsigned> lineBreaks;
- lineBreaks.reserve(4); // A reasonable guess...?
- QByteArray definition;
- definition.reserve(endOfDefinition - startOfDefinition);
- while (startOfDefinition != endOfDefinition) {
- bool replace = false;
- if (*startOfDefinition == '\n'
- || (startOfDefinition + 1 != endOfDefinition
- && *startOfDefinition == '\\'
- && *(startOfDefinition + 1) == '\n')) {
- replace = true;
- if (*startOfDefinition != '\n')
- ++startOfDefinition;
- }
- if (replace) {
- definition.append(' ');
- lineBreaks.append(definition.length() - 1);
- } else {
- definition.append(*startOfDefinition);
- }
- ++startOfDefinition;
- }
- macro.setDefinition(definition.trimmed());
- macro.setLineBreaks(lineBreaks);
+ int start = firstBodyToken.offset;
+ int len = tk->offset - start;
+ QByteArray bodyText = firstBodyToken.source().mid(start, len).trimmed();
+ for (int i = 0, count = bodyTokens.size(); i < count; ++i) {
+ PPToken &t = bodyTokens[i];
+ if (t.isValid())
+ t.squeeze();
}
+ macro.setDefinition(bodyText, bodyTokens);
}
- env->bind(macro);
+ macro.setLength(tk->offset - defineOffset);
+ m_env->bind(macro);
+
+// qDebug() << "adding macro" << macro.name() << "defined at" << macro.fileName() << ":"<<macro.line();
- if (client)
- client->macroAdded(macro);
+ if (m_client)
+ m_client->macroAdded(macro);
}
-void Preprocessor::processIf(TokenIterator firstToken, TokenIterator lastToken)
+QByteArray Preprocessor::expand(PPToken *tk, PPToken *lastConditionToken)
{
- RangeLexer tk(firstToken, lastToken);
-
- ++tk; // skip T_POUND
- ++tk; // skipt `if'
-
- if (testIfLevel()) {
- const char *first = startOfToken(*tk);
- const char *last = startOfToken(*lastToken);
+ QByteArray condition;
+ condition.reserve(256);
+ while (isValidToken(*tk)) {
+ const ByteArrayRef s = tk->asByteArrayRef();
+ condition.append(s.start(), s.length());
+ condition += ' ';
+ if (lastConditionToken)
+ *lastConditionToken = *tk;
+ lex(tk);
+ }
+// qDebug("*** Condition before: [%s]", condition.constData());
- MacroExpander expandCondition (env, 0, client, tk.dot()->offset);
- QByteArray condition;
- condition.reserve(256);
- expandCondition(first, last, &condition);
+ QByteArray result;
+ result.reserve(256);
- QVector<Token> tokens = tokenize(condition);
+ preprocess(m_state.m_currentFileName, condition, &result, true, false, true);
+ result.squeeze();
+// qDebug("*** Condition after: [%s]", result.constData());
+ return result;
+}
- const Value result = evalExpression(tokens.constBegin(),
- tokens.constEnd() - 1,
- condition);
+const PPToken Preprocessor::evalExpression(PPToken *tk, Value &result)
+{
+ PPToken lastConditionToken;
+ const QByteArray expanded = expand(tk, &lastConditionToken);
+ Lexer lexer(expanded.constData(), expanded.constData() + expanded.size());
+ std::vector<Token> buf;
+ Token t;
+ do {
+ lexer.scan(&t);
+ buf.push_back(t);
+ } while (t.isNot(T_EOF_SYMBOL));
+ ExpressionEvaluator eval(m_client, m_env);
+ result = eval(&buf[0], &buf[buf.size() - 1], expanded);
+ return lastConditionToken;
+}
- _trueTest[iflevel] = ! result.is_zero ();
- _skipping[iflevel] = result.is_zero ();
+void Preprocessor::handleIfDirective(PPToken *tk)
+{
+ lex(tk); // consume "if" token
+ Value result;
+ const PPToken lastExpressionToken = evalExpression(tk, result);
+ const bool value = !result.is_zero();
+
+ const bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
+ ++m_state.m_ifLevel;
+ m_state.m_trueTest[m_state.m_ifLevel] = value;
+ if (wasSkipping) {
+ m_state.m_skipping[m_state.m_ifLevel] = wasSkipping;
+ } else {
+ bool startSkipping = !value;
+ m_state.m_skipping[m_state.m_ifLevel] = startSkipping;
+ if (startSkipping && m_client)
+ startSkippingBlocks(lastExpressionToken);
}
+
}
-void Preprocessor::processElse(TokenIterator firstToken, TokenIterator lastToken)
+void Preprocessor::handleElifDirective(PPToken *tk, const PPToken &poundToken)
{
- RangeLexer tk(firstToken, lastToken);
-
- if (iflevel == 0 && !skipping ()) {
- // std::cerr << "*** WARNING #else without #if" << std::endl;
- } else if (iflevel > 0 && _skipping[iflevel - 1]) {
- _skipping[iflevel] = true;
+ if (m_state.m_ifLevel == 0) {
+// std::cerr << "*** WARNING #elif without #if" << std::endl;
+ handleIfDirective(tk);
} else {
- _skipping[iflevel] = _trueTest[iflevel];
+ lex(tk); // consume "elif" token
+ if (m_state.m_skipping[m_state.m_ifLevel - 1]) {
+ // we keep on skipping because we are nested in a skipped block
+ m_state.m_skipping[m_state.m_ifLevel] = true;
+ } else if (m_state.m_trueTest[m_state.m_ifLevel]) {
+ if (!m_state.m_skipping[m_state.m_ifLevel]) {
+ // start skipping because the preceeding then-part was not skipped
+ m_state.m_skipping[m_state.m_ifLevel] = true;
+ if (m_client)
+ startSkippingBlocks(poundToken);
+ }
+ } else {
+ // preceeding then-part was skipped, so calculate if we should start
+ // skipping, depending on the condition
+ Value result;
+ evalExpression(tk, result);
+
+ bool startSkipping = result.is_zero();
+ m_state.m_trueTest[m_state.m_ifLevel] = !startSkipping;
+ m_state.m_skipping[m_state.m_ifLevel] = startSkipping;
+ if (m_client && !startSkipping)
+ m_client->stopSkippingBlocks(poundToken.offset - 1);
+ }
}
}
-void Preprocessor::processElif(TokenIterator firstToken, TokenIterator lastToken)
+void Preprocessor::handleElseDirective(PPToken *tk, const PPToken &poundToken)
{
- RangeLexer tk(firstToken, lastToken);
- ++tk; // skip T_POUND
- ++tk; // skipt `elif'
-
- if (! (iflevel > 0)) {
- // std::cerr << "*** WARNING: " << __FILE__ << __LINE__ << std::endl;
- } else if (iflevel == 0 && !skipping()) {
- // std::cerr << "*** WARNING #else without #if" << std::endl;
- } else if (!_trueTest[iflevel] && !_skipping[iflevel - 1]) {
-
- const char *first = startOfToken(*tk);
- const char *last = startOfToken(*lastToken);
-
- MacroExpander expandCondition (env, 0, client, tk.dot()->offset);
- QByteArray condition;
- condition.reserve(256);
- expandCondition(first, last, &condition);
-
- QVector<Token> tokens = tokenize(condition);
-
- const Value result = evalExpression(tokens.constBegin(),
- tokens.constEnd() - 1,
- condition);
+ lex(tk); // consume "else" token
- _trueTest[iflevel] = ! result.is_zero ();
- _skipping[iflevel] = result.is_zero ();
- } else {
- _skipping[iflevel] = true;
+ if (m_state.m_ifLevel != 0) {
+ if (m_state.m_skipping[m_state.m_ifLevel - 1]) {
+ // we keep on skipping because we are nested in a skipped block
+ m_state.m_skipping[m_state.m_ifLevel] = true;
+ } else {
+ bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
+ bool startSkipping = m_state.m_trueTest[m_state.m_ifLevel];
+ m_state.m_skipping[m_state.m_ifLevel] = startSkipping;
+
+ if (m_client && wasSkipping && !startSkipping)
+ m_client->stopSkippingBlocks(poundToken.offset - 1);
+ else if (m_client && !wasSkipping && startSkipping)
+ startSkippingBlocks(poundToken);
+ }
}
+#ifndef NO_DEBUG
+ else {
+ std::cerr << "*** WARNING #else without #if" << std::endl;
+ }
+#endif // NO_DEBUG
}
-void Preprocessor::processEndif(TokenIterator, TokenIterator)
+void Preprocessor::handleEndIfDirective(PPToken *tk, const PPToken &poundToken)
{
- if (iflevel == 0 && !skipping()) {
- // std::cerr << "*** WARNING #endif without #if" << std::endl;
+ if (m_state.m_ifLevel == 0) {
+#ifndef NO_DEBUG
+ std::cerr << "*** WARNING #endif without #if";
+ if (!tk->generated())
+ std::cerr << " on line " << tk->lineno << " of file " << m_state.m_currentFileName.toUtf8().constData();
+ std::cerr << std::endl;
+#endif // NO_DEBUG
} else {
- _skipping[iflevel] = false;
- _trueTest[iflevel] = false;
-
- --iflevel;
+ bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
+ m_state.m_skipping[m_state.m_ifLevel] = false;
+ m_state.m_trueTest[m_state.m_ifLevel] = false;
+ --m_state.m_ifLevel;
+ if (m_client && wasSkipping && !m_state.m_skipping[m_state.m_ifLevel])
+ m_client->stopSkippingBlocks(poundToken.offset - 1);
}
+
+ lex(tk); // consume "endif" token
}
-void Preprocessor::processIfdef(bool checkUndefined,
- TokenIterator firstToken,
- TokenIterator lastToken)
+void Preprocessor::handleIfDefDirective(bool checkUndefined, PPToken *tk)
{
- RangeLexer tk(firstToken, lastToken);
-
- ++tk; // skip T_POUND
- ++tk; // skip `ifdef'
- if (testIfLevel()) {
- if (tk->is(T_IDENTIFIER)) {
- const QByteArray macroName = tokenSpell(*tk);
+ static const QByteArray qCreatorRun("Q_CREATOR_RUN");
- bool value = false;
- if (Macro *macro = macroDefinition(macroName, tk->offset, env, client)) {
- value = true;
+ lex(tk); // consume "ifdef" token
+ if (tk->is(T_IDENTIFIER)) {
+ bool value = false;
+ const ByteArrayRef macroName = tk->asByteArrayRef();
+ if (Macro *macro = macroDefinition(macroName.toByteArray(), tk->offset, m_env, m_client)) {
+ value = true;
- // the macro is a feature constraint(e.g. QT_NO_XXX)
- if (checkUndefined && macroName.startsWith("QT_NO_")) {
- if (macro->fileName() == QLatin1String("<configuration>")) {
- // and it' defined in a pro file (e.g. DEFINES += QT_NO_QOBJECT)
+ // the macro is a feature constraint(e.g. QT_NO_XXX)
+ if (checkUndefined && macroName.startsWith("QT_NO_")) {
+ if (macro->fileName() == QLatin1String("<configuration>")) {
+ // and it' defined in a pro file (e.g. DEFINES += QT_NO_QOBJECT)
- value = false; // take the branch
- }
+ value = false; // take the branch
}
-
- } else if (env->isBuiltinMacro(macroName)) {
- value = true;
- } else if (macroName == "Q_CREATOR_RUN") {
- value = true;
}
-
- if (checkUndefined)
- value = ! value;
-
- _trueTest[iflevel] = value;
- _skipping [iflevel] = ! value;
+ } else if (m_env->isBuiltinMacro(macroName)) {
+ value = true;
+ } else if (macroName == qCreatorRun) {
+ value = true;
}
- }
-}
-void Preprocessor::processUndef(TokenIterator firstToken, TokenIterator lastToken)
-{
- RangeLexer tk(firstToken, lastToken);
+ if (checkUndefined)
+ value = !value;
- ++tk; // skip T_POUND
- ++tk; // skip `undef'
+ const bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
+ ++m_state.m_ifLevel;
+ m_state.m_trueTest[m_state.m_ifLevel] = value;
+ m_state.m_skipping[m_state.m_ifLevel] = wasSkipping ? wasSkipping : !value;
- if (tk->is(T_IDENTIFIER)) {
- const QByteArray macroName = tokenText(*tk);
- const Macro *macro = env->remove(macroName);
+ if (m_client && !wasSkipping && !value)
+ startSkippingBlocks(*tk);
- if (client && macro)
- client->macroAdded(*macro);
+ lex(tk); // consume the identifier
}
-}
-
-void Preprocessor::resetIfLevel ()
-{
- iflevel = 0;
- _skipping[iflevel] = false;
- _trueTest[iflevel] = false;
-}
-
-Preprocessor::PP_DIRECTIVE_TYPE Preprocessor::classifyDirective(const QByteArray &directive) const
-{
- switch (directive.size())
- {
- case 2:
- if (directive[0] == 'i' && directive[1] == 'f')
- return PP_IF;
- break;
-
- case 4:
- if (directive[0] == 'e' && directive == "elif")
- return PP_ELIF;
- else if (directive[0] == 'e' && directive == "else")
- return PP_ELSE;
- break;
-
- case 5:
- if (directive[0] == 'i' && directive == "ifdef")
- return PP_IFDEF;
- else if (directive[0] == 'u' && directive == "undef")
- return PP_UNDEF;
- else if (directive[0] == 'e' && directive == "endif")
- return PP_ENDIF;
- break;
-
- case 6:
- if (directive[0] == 'i' && directive == "ifndef")
- return PP_IFNDEF;
- else if (directive[0] == 'i' && directive == "import")
- return PP_IMPORT;
- else if (directive[0] == 'd' && directive == "define")
- return PP_DEFINE;
- break;
-
- case 7:
- if (directive[0] == 'i' && directive == "include")
- return PP_INCLUDE;
- break;
-
- case 12:
- if (directive[0] == 'i' && directive == "include_next")
- return PP_INCLUDE_NEXT;
- break;
-
- default:
- break;
+#ifndef NO_DEBUG
+ else {
+ std::cerr << "*** WARNING #ifdef without identifier" << std::endl;
}
-
- return PP_UNKNOWN_DIRECTIVE;
+#endif // NO_DEBUG
}
-bool Preprocessor::testIfLevel()
+void Preprocessor::handleUndefDirective(PPToken *tk)
{
- const bool result = !_skipping[iflevel++];
- _skipping[iflevel] = _skipping[iflevel - 1];
- _trueTest[iflevel] = false;
- return result;
-}
-
-int Preprocessor::skipping() const
-{ return _skipping[iflevel]; }
+ lex(tk); // consume "undef" token
+ if (tk->is(T_IDENTIFIER)) {
+ const ByteArrayRef macroName = tk->asByteArrayRef();
+ const Macro *macro = m_env->remove(macroName.toByteArray());
-Value Preprocessor::evalExpression(TokenIterator firstToken, TokenIterator lastToken,
- const QByteArray &source) const
-{
- ExpressionEvaluator eval(client, env);
- const Value result = eval(firstToken, lastToken, source);
- return result;
+ if (m_client && macro)
+ m_client->macroAdded(*macro);
+ lex(tk); // consume macro name
+ }
+#ifndef NO_DEBUG
+ else {
+ std::cerr << "*** WARNING #undef without identifier" << std::endl;
+ }
+#endif // NO_DEBUG
}
-bool Preprocessor::isQtReservedWord(const QByteArray &macroId) const
+bool Preprocessor::isQtReservedWord(const ByteArrayRef &macroId)
{
const int size = macroId.size();
if (size == 9 && macroId.at(0) == 'Q' && macroId == "Q_SIGNALS")
@@ -1525,9 +1524,63 @@ bool Preprocessor::isQtReservedWord(const QByteArray &macroId) const
QString Preprocessor::string(const char *first, int length) const
{
- if (_originalSource.isEmpty())
+ if (m_originalSource.isEmpty())
return QString::fromUtf8(first, length);
- const int position = first - _source.constData();
- return _originalSource.mid(position, length);
+ const int position = first - m_state.m_source.constData();
+ return m_originalSource.mid(position, length);
+}
+
+PPToken Preprocessor::generateToken(enum Kind kind, const ByteArrayRef &content, unsigned lineno, bool addQuotes)
+{
+ size_t len = content.size();
+ const size_t pos = m_scratchBuffer.size();
+
+ if (kind == T_STRING_LITERAL && addQuotes)
+ m_scratchBuffer.append('"');
+ m_scratchBuffer.append(content.start(), content.length());
+ if (kind == T_STRING_LITERAL && addQuotes) {
+ m_scratchBuffer.append('"');
+ len += 2;
+ }
+
+ PPToken tok(m_scratchBuffer);
+ tok.f.kind = kind;
+ if (m_state.m_lexer->control()) {
+ if (kind == T_STRING_LITERAL)
+ tok.string = m_state.m_lexer->control()->stringLiteral(m_scratchBuffer.constData() + pos, len);
+ else if (kind == T_IDENTIFIER)
+ tok.identifier = m_state.m_lexer->control()->identifier(m_scratchBuffer.constData() + pos, len);
+ else if (kind == T_NUMERIC_LITERAL)
+ tok.number = m_state.m_lexer->control()->numericLiteral(m_scratchBuffer.constData() + pos, len);
+ }
+ tok.offset = pos;
+ tok.f.length = len;
+ tok.f.generated = true;
+ tok.lineno = lineno;
+ return tok;
+}
+
+PPToken Preprocessor::generateConcatenated(const PPToken &leftTk, const PPToken &rightTk)
+{
+ QByteArray newText;
+ newText.reserve(leftTk.length() + rightTk.length());
+ newText.append(leftTk.start(), leftTk.length());
+ newText.append(rightTk.start(), rightTk.length());
+ return generateToken(T_IDENTIFIER, ByteArrayRef(&newText), leftTk.lineno, true);
+}
+
+void Preprocessor::startSkippingBlocks(const Preprocessor::PPToken &tk) const
+{
+ if (!m_client)
+ return;
+
+ int iter = tk.end();
+ const QByteArray &txt = tk.source();
+ for (; iter < txt.size(); ++iter) {
+ if (txt.at(iter) == '\n') {
+ m_client->startSkippingBlocks(iter + 1);
+ return;
+ }
+ }
}
diff --git a/src/libs/cplusplus/pp-engine.h b/src/libs/cplusplus/pp-engine.h
index 3240d3fe9e..67fa9e1141 100644
--- a/src/libs/cplusplus/pp-engine.h
+++ b/src/libs/cplusplus/pp-engine.h
@@ -52,29 +52,39 @@
#ifndef CPLUSPLUS_PP_ENGINE_H
#define CPLUSPLUS_PP_ENGINE_H
+#include "PPToken.h"
#include "PreprocessorClient.h"
-#include "pp-macro-expander.h"
+#include <Lexer.h>
#include <Token.h>
#include <QVector>
#include <QBitArray>
+#include <QByteArray>
namespace CPlusPlus {
-struct Value;
class Environment;
+namespace Internal {
+class PPToken;
+struct TokenBuffer;
+struct Value;
+}
+
class CPLUSPLUS_EXPORT Preprocessor
{
+ typedef Internal::PPToken PPToken;
+ typedef Internal::Value Value;
+
public:
Preprocessor(Client *client, Environment *env);
QByteArray operator()(const QString &filename, const QString &source);
- QByteArray operator()(const QString &filename, const QByteArray &source);
+ QByteArray operator()(const QString &filename, const QByteArray &source, bool noLines = false, bool markGeneratedTokens = true);
void preprocess(const QString &filename,
const QByteArray &source,
- QByteArray *result);
+ QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition);
bool expandMacros() const;
void setExpandMacros(bool expandMacros);
@@ -85,125 +95,107 @@ public:
private:
enum { MAX_LEVEL = 512 };
- enum PP_DIRECTIVE_TYPE
- {
- PP_UNKNOWN_DIRECTIVE,
- PP_DEFINE,
- PP_IMPORT,
- PP_INCLUDE,
- PP_INCLUDE_NEXT,
- PP_ELIF,
- PP_ELSE,
- PP_ENDIF,
- PP_IF,
- PP_IFDEF,
- PP_IFNDEF,
- PP_UNDEF
- };
+ struct State {
+ State();
- typedef const CPlusPlus::Token *TokenIterator;
+ void pushTokenBuffer(const PPToken *start, const PPToken *end, const Macro *macro);
+ void popTokenBuffer();
- struct State {
- QByteArray source;
- QVector<CPlusPlus::Token> tokens;
- TokenIterator dot;
- };
+ QString m_currentFileName;
- bool markGeneratedTokens(bool markGeneratedTokens, TokenIterator dot = 0);
+ QByteArray m_source;
+ Lexer *m_lexer;
+ QBitArray m_skipping;
+ QBitArray m_trueTest;
+ int m_ifLevel;
+ Internal::TokenBuffer *m_tokenBuffer;
+ unsigned m_tokenBufferDepth;
+ bool m_inPreprocessorDirective;
- QByteArray expand(const QByteArray &source);
- void expand(const QByteArray &source, QByteArray *result);
- void expand(const char *first, const char *last, QByteArray *result);
- void expandBuiltinMacro(TokenIterator identifierToken,
- const QByteArray &spell);
- void expandObjectLikeMacro(TokenIterator identifierToken,
- const QByteArray &spell,
- Macro *m, QByteArray *result);
- void expandFunctionLikeMacro(TokenIterator identifierToken, Macro *m,
- const QVector<MacroArgumentReference> &actuals);
+ QByteArray *m_result;
+ bool m_markGeneratedTokens;
- void resetIfLevel();
- bool testIfLevel();
- int skipping() const;
+ bool m_noLines;
+ bool m_inCondition;
+ bool m_inDefine;
+ };
- PP_DIRECTIVE_TYPE classifyDirective(const QByteArray &directive) const;
+ void handleDefined(PPToken *tk);
+ void pushToken(PPToken *tk);
+ void lex(PPToken *tk);
+ void skipPreprocesorDirective(PPToken *tk);
+ bool handleIdentifier(PPToken *tk);
+ bool handleFunctionLikeMacro(PPToken *tk, const Macro *macro, QVector<PPToken> &body, bool addWhitespaceMarker);
- Value evalExpression(TokenIterator firstToken,
- TokenIterator lastToken,
- const QByteArray &source) const;
+ bool skipping() const
+ { return m_state.m_skipping[m_state.m_ifLevel]; }
QVector<CPlusPlus::Token> tokenize(const QByteArray &text) const;
- const char *startOfToken(const CPlusPlus::Token &token) const;
- const char *endOfToken(const CPlusPlus::Token &token) const;
+ bool collectActualArguments(PPToken *tk, QVector<QVector<PPToken> > *actuals);
+ void scanActualArgument(PPToken *tk, QVector<PPToken> *tokens);
- QByteArray tokenSpell(const CPlusPlus::Token &token) const;
- QByteArray tokenText(const CPlusPlus::Token &token) const; // does a deep copy
+ void handlePreprocessorDirective(PPToken *tk);
+ void handleIncludeDirective(PPToken *tk);
+ void handleDefineDirective(PPToken *tk);
+ QByteArray expand(PPToken *tk, PPToken *lastConditionToken = 0);
+ const Internal::PPToken evalExpression(PPToken *tk, Value &result);
+ void handleIfDirective(PPToken *tk);
+ void handleElifDirective(PPToken *tk, const PPToken &poundToken);
+ void handleElseDirective(PPToken *tk, const PPToken &poundToken);
+ void handleEndIfDirective(PPToken *tk, const PPToken &poundToken);
+ void handleIfDefDirective(bool checkUndefined, PPToken *tk);
+ void handleUndefDirective(PPToken *tk);
- void collectActualArguments(QVector<MacroArgumentReference> *actuals);
- MacroArgumentReference collectOneActualArgument();
+ static bool isQtReservedWord(const Internal::ByteArrayRef &name);
- void processNewline(bool force = false);
+ void pushState(const State &newState);
+ void popState();
- void processSkippingBlocks(bool skippingBlocks,
- TokenIterator dot, TokenIterator lastToken);
+ State createStateFromSource(const QString &fileName, const QByteArray &source, QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition) const;
- Macro *processObjectLikeMacro(TokenIterator identifierToken,
- const QByteArray &spell,
- Macro *m);
+ inline bool atStartOfOutputLine() const
+ { return (m_state.m_result && !m_state.m_result->isEmpty()) ? m_state.m_result->end()[-1] == '\n' : true; }
- void processDirective(TokenIterator dot, TokenIterator lastToken);
- void processInclude(bool skipCurrentPath,
- TokenIterator dot, TokenIterator lastToken,
- bool acceptMacros = true);
- void processDefine(TokenIterator dot, TokenIterator lastToken);
- void processIf(TokenIterator dot, TokenIterator lastToken);
- void processElse(TokenIterator dot, TokenIterator lastToken);
- void processElif(TokenIterator dot, TokenIterator lastToken);
- void processEndif(TokenIterator dot, TokenIterator lastToken);
- void processIfdef(bool checkUndefined,
- TokenIterator dot, TokenIterator lastToken);
- void processUndef(TokenIterator dot, TokenIterator lastToken);
+ inline void startNewOutputLine() const
+ {
+ if (m_state.m_result && !m_state.m_result->isEmpty() && m_state.m_result->end()[-1] != '\n')
+ out('\n');
+ }
- bool isQtReservedWord(const QByteArray &name) const;
+ void genLine(unsigned lineno, const QByteArray &fileName) const;
- State state() const;
- void pushState(const State &state);
- void popState();
+ inline void out(const QByteArray &text) const
+ { if (m_state.m_result) m_state.m_result->append(text); }
- State createStateFromSource(const QByteArray &source) const;
+ inline void out(char ch) const
+ { if (m_state.m_result) m_state.m_result->append(ch); }
- void out(const QByteArray &text);
- void out(char ch);
- void out(const char *s);
+ inline void out(const char *s) const
+ { if (m_state.m_result) m_state.m_result->append(s); }
- QString string(const char *first, int len) const;
- bool maybeAfterComment() const;
+ inline void out(const Internal::ByteArrayRef &ref) const
+ { if (m_state.m_result) m_state.m_result->append(ref.start(), ref.length()); }
- bool maybeMultilineToken(TokenIterator tok);
- void skipToNextLine();
+ QString string(const char *first, int len) const;
-private:
- Client *client;
- Environment *env;
- MacroExpander _expand;
+ PPToken generateToken(enum Kind kind, const Internal::ByteArrayRef &content, unsigned lineno, bool addQuotes);
+ PPToken generateConcatenated(const PPToken &leftTk, const PPToken &rightTk);
- QBitArray _skipping; // ### move in state
- QBitArray _trueTest; // ### move in state
- int iflevel; // ### move in state
+ void startSkippingBlocks(const PPToken &tk) const;
- QList<State> _savedStates;
+private:
+ Client *m_client;
+ Environment *m_env;
+ QByteArray m_scratchBuffer;
- QByteArray _source;
- QVector<CPlusPlus::Token> _tokens;
- TokenIterator _dot;
+ QList<State> m_savedStates;
- QByteArray *_result;
- bool _markGeneratedTokens;
+ QString m_originalSource;
+ bool m_expandMacros;
+ bool m_keepComments;
- QString _originalSource;
- bool _expandMacros;
- bool _keepComments;
+ State m_state;
};
} // namespace CPlusPlus
diff --git a/src/libs/cplusplus/pp-macro-expander.cpp b/src/libs/cplusplus/pp-macro-expander.cpp
deleted file mode 100644
index 87c7b25e24..0000000000
--- a/src/libs/cplusplus/pp-macro-expander.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "pp-macro-expander.h"
-
-#include "pp.h"
-#include "pp-cctype.h"
-#include <QDateTime>
-
-namespace CPlusPlus {
-
-
-
-struct pp_frame
-{
- Macro *expanding_macro;
- const QVector<QByteArray> actuals;
-
- pp_frame(Macro *expanding_macro, const QVector<QByteArray> &actuals)
- : expanding_macro (expanding_macro),
- actuals (actuals)
- { }
-};
-
-
-} // namespace CPlusPlus
-
-using namespace CPlusPlus;
-
-inline static bool comment_p (const char *__first, const char *__last)
-{
- if (__first == __last)
- return false;
-
- if (*__first != '/')
- return false;
-
- if (++__first == __last)
- return false;
-
- return (*__first == '/' || *__first == '*');
-}
-
-MacroExpander::MacroExpander(Environment *env, pp_frame *frame, Client *client, unsigned start_offset)
- : env(env),
- frame(frame),
- client(client),
- start_offset(start_offset),
- lines(0)
-{ }
-
-const QByteArray *MacroExpander::resolve_formal(const QByteArray &__name)
-{
- if (! (frame && frame->expanding_macro))
- return 0;
-
- const QVector<QByteArray> formals = frame->expanding_macro->formals();
- for (int index = 0; index < formals.size(); ++index) {
- const QByteArray formal = formals.at(index);
-
- if (formal == __name && index < frame->actuals.size())
- return &frame->actuals.at(index);
- }
-
- return 0;
-}
-
-const char *MacroExpander::operator()(const char *first, const char *last,
- QByteArray *result)
-{
- return expand(first, last, result);
-}
-
-const char *MacroExpander::expand(const char *__first, const char *__last,
- QByteArray *__result)
-{
- const char *start = __first;
- __first = skip_blanks (__first, __last);
- lines = skip_blanks.lines;
-
- while (__first != __last)
- {
- if (*__first == '\n')
- {
- __result->append("\n# ");
- __result->append(QByteArray::number(env->currentLine));
- __result->append(' ');
- __result->append('"');
- __result->append(env->currentFile.toUtf8());
- __result->append('"');
- __result->append('\n');
- ++lines;
-
- __first = skip_blanks (++__first, __last);
- lines += skip_blanks.lines;
-
- if (__first != __last && *__first == '#')
- break;
- }
- else if (*__first == '#')
- {
- __first = skip_blanks (++__first, __last);
- lines += skip_blanks.lines;
-
- const char *end_id = skip_identifier (__first, __last);
- const QByteArray fast_name(__first, end_id - __first);
- __first = end_id;
-
- if (const QByteArray *actual = resolve_formal (fast_name))
- {
- __result->append('\"');
-
- const char *actual_begin = actual->constData ();
- const char *actual_end = actual_begin + actual->size ();
-
- for (const char *it = skip_whitespaces (actual_begin, actual_end);
- it != actual_end; ++it)
- {
- if (*it == '"' || *it == '\\')
- {
- __result->append('\\');
- __result->append(*it);
- }
- else if (*it == '\n')
- {
- __result->append('"');
- __result->append('\n');
- __result->append('"');
- }
- else
- __result->append(*it);
- }
-
- __result->append('\"');
- }
- else {
- // ### warning message?
- }
- }
- else if (*__first == '\"')
- {
- const char *next_pos = skip_string_literal (__first, __last);
- lines += skip_string_literal.lines;
- __result->append(__first, next_pos - __first);
- __first = next_pos;
- }
- else if (*__first == '\'')
- {
- const char *next_pos = skip_char_literal (__first, __last);
- lines += skip_char_literal.lines;
- __result->append(__first, next_pos - __first);
- __first = next_pos;
- }
- else if (*__first == '\\')
- {
- ++__first;
- if (__first != __last && *__first == '\n')
- {
- ++lines;
- ++__first;
- } else {
- __result->append('\\');
- }
- }
- else if (comment_p (__first, __last))
- {
- __first = skip_comment_or_divop (__first, __last);
- int n = skip_comment_or_divop.lines;
- lines += n;
-
- while (n-- > 0)
- __result->append('\n');
- }
- else if (pp_isspace (*__first))
- {
- for (; __first != __last; ++__first)
- {
- if (*__first == '\n' || !pp_isspace (*__first))
- break;
- }
-
- __result->append(' ');
- }
- else if (pp_isdigit (*__first))
- {
- const char *next_pos = skip_number (__first, __last);
- lines += skip_number.lines;
- __result->append(__first, next_pos - __first);
- __first = next_pos;
- }
- else if (pp_isalpha (*__first) || *__first == '_')
- {
- const char *name_begin = __first;
- const char *name_end = skip_identifier (__first, __last);
- __first = name_end; // advance
-
- // search for the paste token
- const char *next = skip_blanks (__first, __last);
- bool paste = false;
-
- bool need_comma = false;
- if (next != __last && *next == ',') {
- const char *x = skip_blanks(__first + 1, __last);
- if (x != __last && *x == '#' && (x + 1) != __last && x[1] == '#') {
- need_comma = true;
- paste = true;
- __first = skip_blanks(x + 2, __last);
- }
- }
-
- if (next != __last && *next == '#')
- {
- paste = true;
- ++next;
- if (next != __last && *next == '#')
- __first = skip_blanks(++next, __last);
- }
-
- const QByteArray fast_name(name_begin, name_end - name_begin);
- const QByteArray *actual = resolve_formal (fast_name);
- if (actual)
- {
- const char *begin = actual->constData ();
- const char *end = begin + actual->size ();
- if (paste) {
- for (--end; end != begin - 1; --end) {
- if (! pp_isspace(*end))
- break;
- }
- ++end;
- }
- __result->append(begin, end - begin);
- if (need_comma)
- __result->append(',');
- continue;
- }
-
- Macro *macro = env->resolve (fast_name);
- if (! macro || macro->isHidden() || env->hideNext)
- {
- if (fast_name.size () == 7 && fast_name [0] == 'd' && fast_name == "defined")
- env->hideNext = true;
- else
- env->hideNext = false;
-
- if (fast_name.size () == 8 && fast_name [0] == '_' && fast_name [1] == '_')
- {
- if (fast_name == "__LINE__")
- {
- __result->append(QByteArray::number(env->currentLine + lines));
- continue;
- }
-
- else if (fast_name == "__FILE__")
- {
- __result->append('"');
- __result->append(env->currentFile.toUtf8());
- __result->append('"');
- continue;
- }
-
- else if (fast_name == "__DATE__")
- {
- __result->append('"');
- __result->append(QDate::currentDate().toString().toUtf8());
- __result->append('"');
- continue;
- }
-
- else if (fast_name == "__TIME__")
- {
- __result->append('"');
- __result->append(QTime::currentTime().toString().toUtf8());
- __result->append('"');
- continue;
- }
-
- }
-
- __result->append(name_begin, name_end - name_begin);
- continue;
- }
-
- if (! macro->isFunctionLike())
- {
- Macro *m = 0;
-
- if (! macro->definition().isEmpty())
- {
- macro->setHidden(true);
-
- QByteArray __tmp;
- __tmp.reserve (256);
-
- MacroExpander expand_macro (env);
- expand_macro(macro->definition(), &__tmp);
-
- if (! __tmp.isEmpty ())
- {
- const char *__tmp_begin = __tmp.constBegin();
- const char *__tmp_end = __tmp.constEnd();
- const char *__begin_id = skip_whitespaces (__tmp_begin, __tmp_end);
- const char *__end_id = skip_identifier (__begin_id, __tmp_end);
-
- if (__end_id == __tmp_end)
- {
- const QByteArray __id (__begin_id, __end_id - __begin_id);
- m = env->resolve (__id);
- }
-
- if (! m)
- *__result += __tmp;
- }
-
- macro->setHidden(false);
- }
-
- if (! m)
- continue;
-
- macro = m;
- }
-
- // function like macro
- const char *arg_it = skip_whitespaces (__first, __last);
-
- if (arg_it == __last || *arg_it != '(')
- {
- __result->append(name_begin, name_end - name_begin);
- lines += skip_whitespaces.lines;
- __first = arg_it;
- continue;
- }
-
- QVector<QByteArray> actuals;
- QVector<MacroArgumentReference> actuals_ref;
- actuals.reserve (5);
- ++arg_it; // skip '('
-
- MacroExpander expand_actual (env, frame);
-
- const char *arg_end = skip_argument_variadics (actuals, macro, arg_it, __last);
- if (arg_it != arg_end)
- {
- actuals_ref.append(MacroArgumentReference(start_offset + (arg_it-start), arg_end - arg_it));
- const QByteArray actual (arg_it, arg_end - arg_it);
- QByteArray expanded;
- expand_actual (actual.constBegin (), actual.constEnd (), &expanded);
- actuals.push_back (expanded);
- arg_it = arg_end;
- }
-
- while (arg_it != __last && *arg_end == ',')
- {
- ++arg_it; // skip ','
-
- arg_end = skip_argument_variadics (actuals, macro, arg_it, __last);
- actuals_ref.append(MacroArgumentReference(start_offset + (arg_it-start), arg_end - arg_it));
- const QByteArray actual (arg_it, arg_end - arg_it);
- QByteArray expanded;
- expand_actual (actual.constBegin (), actual.constEnd (), &expanded);
- actuals.push_back (expanded);
- arg_it = arg_end;
- }
-
- if (! (arg_it != __last && *arg_it == ')'))
- return __last;
-
- ++arg_it; // skip ')'
- __first = arg_it;
-
- pp_frame frame (macro, actuals);
- MacroExpander expand_macro (env, &frame);
- macro->setHidden(true);
- expand_macro (macro->definition(), __result);
- macro->setHidden(false);
- }
- else
- __result->append(*__first++);
- }
-
- return __first;
-}
-
-const char *MacroExpander::skip_argument_variadics (QVector<QByteArray> const &__actuals,
- Macro *__macro,
- const char *__first, const char *__last)
-{
- const char *arg_end = skip_argument (__first, __last);
-
- while (__macro->isVariadic() && __first != arg_end && arg_end != __last && *arg_end == ','
- && (__actuals.size () + 1) == __macro->formals().size ())
- {
- arg_end = skip_argument (++arg_end, __last);
- }
-
- return arg_end;
-}
diff --git a/src/libs/cplusplus/pp-macro-expander.h b/src/libs/cplusplus/pp-macro-expander.h
deleted file mode 100644
index 3352f7a4fc..0000000000
--- a/src/libs/cplusplus/pp-macro-expander.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-/*
- Copyright 2005 Roberto Raggi <roberto@kdevelop.org>
-
- Permission to use, copy, modify, distribute, and sell this software and its
- documentation for any purpose is hereby granted without fee, provided that
- the above copyright notice appear in all copies and that both that
- copyright notice and this permission notice appear in supporting
- documentation.
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef CPLUSPLUS_PP_MACRO_EXPANDER_H
-#define CPLUSPLUS_PP_MACRO_EXPANDER_H
-
-#include "Macro.h"
-#include "pp-scanner.h"
-#include <QVector>
-#include <QByteArray>
-
-namespace CPlusPlus {
-
-class Environment;
-class Client;
-
-struct pp_frame;
-
-class MacroExpander
-{
- Environment *env;
- pp_frame *frame;
- Client *client;
- unsigned start_offset;
-
- pp_skip_number skip_number;
- pp_skip_identifier skip_identifier;
- pp_skip_string_literal skip_string_literal;
- pp_skip_char_literal skip_char_literal;
- pp_skip_argument skip_argument;
- pp_skip_comment_or_divop skip_comment_or_divop;
- pp_skip_blanks skip_blanks;
- pp_skip_whitespaces skip_whitespaces;
-
- const QByteArray *resolve_formal(const QByteArray &name);
-
-public:
- explicit MacroExpander(Environment *env, pp_frame *frame = 0, Client *client = 0, unsigned start_offset = 0);
-
- const char *operator()(const char *first, const char *last,
- QByteArray *result);
-
- const char *operator()(const QByteArray &source,
- QByteArray *result)
- { return operator()(source.constBegin(), source.constEnd(), result); }
-
- const char *expand(const char *first, const char *last,
- QByteArray *result);
-
- const char *skip_argument_variadics(const QVector<QByteArray> &actuals,
- Macro *macro,
- const char *first, const char *last);
-
-public: // attributes
- int lines;
-};
-
-} // namespace CPlusPlus
-
-#endif // CPLUSPLUS_PP_MACRO_EXPANDER_H
-
diff --git a/src/libs/cplusplus/pp.h b/src/libs/cplusplus/pp.h
index bb4d622df4..08f50ecea3 100644
--- a/src/libs/cplusplus/pp.h
+++ b/src/libs/cplusplus/pp.h
@@ -56,7 +56,6 @@
#include "PreprocessorClient.h"
#include "PreprocessorEnvironment.h"
#include "pp-scanner.h"
-#include "pp-macro-expander.h"
#include "pp-engine.h"
#endif // CPLUSPLUS_PREPROCESSOR_H
diff --git a/src/libs/extensionsystem/iplugin.h b/src/libs/extensionsystem/iplugin.h
index 37f474a09c..598eef9aab 100644
--- a/src/libs/extensionsystem/iplugin.h
+++ b/src/libs/extensionsystem/iplugin.h
@@ -36,6 +36,9 @@
#include "extensionsystem_global.h"
#include <QObject>
+#if QT_VERSION >= 0x050000
+# include <QtPlugin>
+#endif
namespace ExtensionSystem {
@@ -83,4 +86,16 @@ private:
} // namespace ExtensionSystem
+// The macros Q_EXPORT_PLUGIN, Q_EXPORT_PLUGIN2 become obsolete in Qt 5.
+#if QT_VERSION >= 0x050000
+# if defined(Q_EXPORT_PLUGIN)
+# undef Q_EXPORT_PLUGIN
+# undef Q_EXPORT_PLUGIN2
+# endif
+# define Q_EXPORT_PLUGIN(plugin)
+# define Q_EXPORT_PLUGIN2(function, plugin)
+#else
+# define Q_PLUGIN_METADATA(x)
+#endif
+
#endif // IPLUGIN_H
diff --git a/src/libs/qmleditorwidgets/qmleditorwidgets-lib.pri b/src/libs/qmleditorwidgets/qmleditorwidgets-lib.pri
index 1faa8eec80..f3b0cea801 100644
--- a/src/libs/qmleditorwidgets/qmleditorwidgets-lib.pri
+++ b/src/libs/qmleditorwidgets/qmleditorwidgets-lib.pri
@@ -1,4 +1,10 @@
-QT += declarative script
+QT += script
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
+
INCLUDEPATH += $$PWD
contains(CONFIG, dll) {
diff --git a/src/libs/qmleditorwidgets/qmleditorwidgets.pri b/src/libs/qmleditorwidgets/qmleditorwidgets.pri
index 55a4c3da0a..4a2788cf3a 100644
--- a/src/libs/qmleditorwidgets/qmleditorwidgets.pri
+++ b/src/libs/qmleditorwidgets/qmleditorwidgets.pri
@@ -1,5 +1,9 @@
INCLUDEPATH += $$PWD $$PWD/easingpane
-QT += declarative
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
LIBS *= -l$$qtLibraryName(QmlEditorWidgets)
diff --git a/src/tools/qmlprofilertool/commandlistener.h b/src/libs/qmljsdebugclient/qdeclarativeengineclient.h
index 4b8df747ff..841ec03a51 100644
--- a/src/tools/qmlprofilertool/commandlistener.h
+++ b/src/libs/qmljsdebugclient/qdeclarativeengineclient.h
@@ -6,7 +6,6 @@
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
-**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
@@ -30,25 +29,25 @@
**
**************************************************************************/
-#ifndef COMMANDLISTENER_H
-#define COMMANDLISTENER_H
+#ifndef QDECLARATIVEENGINECLIENT_H
+#define QDECLARATIVEENGINECLIENT_H
+
+#include "qmlenginedebugclient.h"
-#include <QThread>
+namespace QmlJsDebugClient {
-class CommandListener : public QThread
+class QDeclarativeDebugConnection;
+
+class QMLJSDEBUGCLIENT_EXPORT QDeclarativeEngineClient : public QmlEngineDebugClient
{
Q_OBJECT
public:
- CommandListener(QObject *parent = 0);
-
- void run();
-
- void requestStop() { m_stopRequested = true; }
-signals:
- void command(const QString &command);
-
-private:
- bool m_stopRequested;
+ QDeclarativeEngineClient(QDeclarativeDebugConnection *conn)
+ : QmlEngineDebugClient(QLatin1String("QDeclarativeEngine"), conn)
+ {
+ }
};
-#endif // COMMANDLISTENER_H
+} // namespace QmlJsDebugClient
+
+#endif // QDECLARATIVEENGINECLIENT_H
diff --git a/src/libs/qmljsdebugclient/qdeclarativeenginedebug.cpp b/src/libs/qmljsdebugclient/qdeclarativeenginedebug.cpp
deleted file mode 100644
index 6f12de8f94..0000000000
--- a/src/libs/qmljsdebugclient/qdeclarativeenginedebug.cpp
+++ /dev/null
@@ -1,1097 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "qdeclarativeenginedebug.h"
-
-#include "qdeclarativedebugclient.h"
-
-namespace QmlJsDebugClient {
-
-class QDeclarativeEngineDebugClient : public QDeclarativeDebugClient
-{
-public:
- QDeclarativeEngineDebugClient(QDeclarativeDebugConnection *client, QDeclarativeEngineDebugPrivate *p);
-
-protected:
- virtual void statusChanged(Status status);
- virtual void messageReceived(const QByteArray &);
-
-private:
- QDeclarativeEngineDebugPrivate *priv;
- friend class QDeclarativeEngineDebugPrivate;
-};
-
-class QDeclarativeEngineDebugPrivate
-{
- // Q_DECLARE_PUBLIC(QDeclarativeEngineDebug)
-public:
- QDeclarativeEngineDebugPrivate(QDeclarativeEngineDebug *, QDeclarativeDebugConnection *);
- ~QDeclarativeEngineDebugPrivate();
-
- void statusChanged(QDeclarativeDebugClient::Status status);
- void message(const QByteArray &);
-
- QDeclarativeEngineDebugClient *client;
- QDeclarativeEngineDebug *q_ptr;
- int nextId;
- int getId();
-
- void decode(QDataStream &, QDeclarativeDebugContextReference &);
- void decode(QDataStream &, QDeclarativeDebugObjectReference &, bool simple);
-
- static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugEnginesQuery *);
- static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugRootContextQuery *);
- static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugObjectQuery *);
- static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugExpressionQuery *);
- static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugWatch *);
-
- QHash<int, QDeclarativeDebugEnginesQuery *> enginesQuery;
- QHash<int, QDeclarativeDebugRootContextQuery *> rootContextQuery;
- QHash<int, QDeclarativeDebugObjectQuery *> objectQuery;
- QHash<int, QDeclarativeDebugExpressionQuery *> expressionQuery;
-
- QHash<int, QDeclarativeDebugWatch *> watched;
-};
-
-QDeclarativeEngineDebugClient::QDeclarativeEngineDebugClient(QDeclarativeDebugConnection *client,
- QDeclarativeEngineDebugPrivate *p)
- : QDeclarativeDebugClient(QLatin1String("QDeclarativeEngine"), client), priv(p)
-{
-}
-
-void QDeclarativeEngineDebugClient::statusChanged(Status status)
-{
- if (priv)
- priv->statusChanged(status);
-}
-
-void QDeclarativeEngineDebugClient::messageReceived(const QByteArray &data)
-{
- if (priv)
- priv->message(data);
-}
-
-QDeclarativeEngineDebugPrivate::QDeclarativeEngineDebugPrivate(QDeclarativeEngineDebug *q, QDeclarativeDebugConnection *c)
- : client(new QDeclarativeEngineDebugClient(c, this)), q_ptr(q), nextId(0)
-{
-}
-
-QDeclarativeEngineDebugPrivate::~QDeclarativeEngineDebugPrivate()
-{
- if (client)
- client->priv = 0;
- delete client;
-
- QHash<int, QDeclarativeDebugEnginesQuery*>::iterator enginesIter = enginesQuery.begin();
- for (; enginesIter != enginesQuery.end(); ++enginesIter) {
- enginesIter.value()->m_client = 0;
- if (enginesIter.value()->state() == QDeclarativeDebugQuery::Waiting)
- enginesIter.value()->setState(QDeclarativeDebugQuery::Error);
- }
-
- QHash<int, QDeclarativeDebugRootContextQuery*>::iterator rootContextIter = rootContextQuery.begin();
- for (; rootContextIter != rootContextQuery.end(); ++rootContextIter) {
- rootContextIter.value()->m_client = 0;
- if (rootContextIter.value()->state() == QDeclarativeDebugQuery::Waiting)
- rootContextIter.value()->setState(QDeclarativeDebugQuery::Error);
- }
-
- QHash<int, QDeclarativeDebugObjectQuery*>::iterator objectIter = objectQuery.begin();
- for (; objectIter != objectQuery.end(); ++objectIter) {
- objectIter.value()->m_client = 0;
- if (objectIter.value()->state() == QDeclarativeDebugQuery::Waiting)
- objectIter.value()->setState(QDeclarativeDebugQuery::Error);
- }
-
- QHash<int, QDeclarativeDebugExpressionQuery*>::iterator exprIter = expressionQuery.begin();
- for (; exprIter != expressionQuery.end(); ++exprIter) {
- exprIter.value()->m_client = 0;
- if (exprIter.value()->state() == QDeclarativeDebugQuery::Waiting)
- exprIter.value()->setState(QDeclarativeDebugQuery::Error);
- }
-
- QHash<int, QDeclarativeDebugWatch*>::iterator watchIter = watched.begin();
- for (; watchIter != watched.end(); ++watchIter) {
- watchIter.value()->m_client = 0;
- watchIter.value()->setState(QDeclarativeDebugWatch::Dead);
- }
-}
-
-int QDeclarativeEngineDebugPrivate::getId()
-{
- return nextId++;
-}
-
-void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugEnginesQuery *q)
-{
- if (c && q) {
- QDeclarativeEngineDebugPrivate *p = c->d_func();
- p->enginesQuery.remove(q->m_queryId);
- }
-}
-
-void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c,
- QDeclarativeDebugRootContextQuery *q)
-{
- if (c && q) {
- QDeclarativeEngineDebugPrivate *p = c->d_func();
- p->rootContextQuery.remove(q->m_queryId);
- }
-}
-
-void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugWatch *w)
-{
- if (c && w) {
- QDeclarativeEngineDebugPrivate *p = c->d_func();
- p->watched.remove(w->m_queryId);
- }
-}
-
-// from qdeclarativeenginedebug_p.h
-struct QDeclarativeObjectData {
- QUrl url;
- int lineNumber;
- int columnNumber;
- QString idString;
- QString objectName;
- QString objectType;
- int objectId;
- int contextId;
-};
-
-QDataStream &operator>>(QDataStream &ds, QDeclarativeObjectData &data)
-{
- ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
- >> data.objectName >> data.objectType >> data.objectId >> data.contextId;
- return ds;
-}
-
-struct QDeclarativeObjectProperty {
- enum Type { Unknown, Basic, Object, List, SignalProperty };
- Type type;
- QString name;
- QVariant value;
- QString valueTypeName;
- QString binding;
- bool hasNotifySignal;
-};
-
-QDataStream &operator>>(QDataStream &ds, QDeclarativeObjectProperty &data)
-{
- int type;
- ds >> type >> data.name >> data.value >> data.valueTypeName
- >> data.binding >> data.hasNotifySignal;
- data.type = (QDeclarativeObjectProperty::Type)type;
- return ds;
-}
-
-void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugObjectQuery *q)
-{
- if (c && q) {
- QDeclarativeEngineDebugPrivate *p = c->d_func();
- p->objectQuery.remove(q->m_queryId);
- }
-}
-
-void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugExpressionQuery *q)
-{
- if (c && q) {
- QDeclarativeEngineDebugPrivate *p = c->d_func();
- p->expressionQuery.remove(q->m_queryId);
- }
-}
-
-void QDeclarativeEngineDebugPrivate::decode(QDataStream &ds, QDeclarativeDebugObjectReference &o,
- bool simple)
-{
- QDeclarativeObjectData data;
- ds >> data;
- o.m_debugId = data.objectId;
- o.m_class = data.objectType;
- o.m_idString = data.idString;
- o.m_name = data.objectName;
- o.m_source.m_url = data.url;
- o.m_source.m_lineNumber = data.lineNumber;
- o.m_source.m_columnNumber = data.columnNumber;
- o.m_contextDebugId = data.contextId;
-
- if (simple)
- return;
-
- int childCount;
- bool recur;
- ds >> childCount >> recur;
-
- for (int ii = 0; ii < childCount; ++ii) {
- o.m_children.append(QDeclarativeDebugObjectReference());
- decode(ds, o.m_children.last(), !recur);
- }
-
- int propCount;
- ds >> propCount;
-
- for (int ii = 0; ii < propCount; ++ii) {
- QDeclarativeObjectProperty data;
- ds >> data;
- QDeclarativeDebugPropertyReference prop;
- prop.m_objectDebugId = o.m_debugId;
- prop.m_name = data.name;
- prop.m_binding = data.binding;
- prop.m_hasNotifySignal = data.hasNotifySignal;
- prop.m_valueTypeName = data.valueTypeName;
- switch (data.type) {
- case QDeclarativeObjectProperty::Basic:
- case QDeclarativeObjectProperty::List:
- case QDeclarativeObjectProperty::SignalProperty:
- {
- prop.m_value = data.value;
- break;
- }
- case QDeclarativeObjectProperty::Object:
- {
- QDeclarativeDebugObjectReference obj;
- obj.m_debugId = prop.m_value.toInt();
- prop.m_value = qVariantFromValue(obj);
- break;
- }
- case QDeclarativeObjectProperty::Unknown:
- break;
- }
- o.m_properties << prop;
- }
-}
-
-void QDeclarativeEngineDebugPrivate::decode(QDataStream &ds, QDeclarativeDebugContextReference &c)
-{
- ds >> c.m_name >> c.m_debugId;
-
- int contextCount;
- ds >> contextCount;
-
- for (int ii = 0; ii < contextCount; ++ii) {
- c.m_contexts.append(QDeclarativeDebugContextReference());
- decode(ds, c.m_contexts.last());
- }
-
- int objectCount;
- ds >> objectCount;
-
- for (int ii = 0; ii < objectCount; ++ii) {
- QDeclarativeDebugObjectReference obj;
- decode(ds, obj, true);
-
- obj.m_contextDebugId = c.m_debugId;
- c.m_objects << obj;
- }
-}
-
-void QDeclarativeEngineDebugPrivate::statusChanged(QDeclarativeDebugClient::Status status)
-{
- emit q_ptr->statusChanged(status);
-}
-
-void QDeclarativeEngineDebugPrivate::message(const QByteArray &data)
-{
- QDataStream ds(data);
-
- QByteArray type;
- ds >> type;
-
- //qDebug() << "QDeclarativeEngineDebugPrivate::message()" << type;
-
- if (type == "LIST_ENGINES_R") {
- int queryId;
- ds >> queryId;
-
- QDeclarativeDebugEnginesQuery *query = enginesQuery.value(queryId);
- if (!query)
- return;
- enginesQuery.remove(queryId);
-
- int count;
- ds >> count;
-
- for (int ii = 0; ii < count; ++ii) {
- QDeclarativeDebugEngineReference ref;
- ds >> ref.m_name;
- ds >> ref.m_debugId;
- query->m_engines << ref;
- }
-
- query->m_client = 0;
- query->setState(QDeclarativeDebugQuery::Completed);
- } else if (type == "LIST_OBJECTS_R") {
- int queryId;
- ds >> queryId;
-
- QDeclarativeDebugRootContextQuery *query = rootContextQuery.value(queryId);
- if (!query)
- return;
- rootContextQuery.remove(queryId);
-
- if (!ds.atEnd())
- decode(ds, query->m_context);
-
- query->m_client = 0;
- query->setState(QDeclarativeDebugQuery::Completed);
- } else if (type == "FETCH_OBJECT_R") {
- int queryId;
- ds >> queryId;
-
- QDeclarativeDebugObjectQuery *query = objectQuery.value(queryId);
- if (!query)
- return;
- objectQuery.remove(queryId);
-
- if (!ds.atEnd())
- decode(ds, query->m_object, false);
-
- query->m_client = 0;
- query->setState(QDeclarativeDebugQuery::Completed);
- } else if (type == "EVAL_EXPRESSION_R") {
- int queryId;
- QVariant result;
- ds >> queryId >> result;
-
- QDeclarativeDebugExpressionQuery *query = expressionQuery.value(queryId);
- if (!query)
- return;
- expressionQuery.remove(queryId);
-
- query->m_result = result;
- query->m_client = 0;
- query->setState(QDeclarativeDebugQuery::Completed);
- } else if (type == "WATCH_PROPERTY_R") {
- int queryId;
- bool ok;
- ds >> queryId >> ok;
-
- QDeclarativeDebugWatch *watch = watched.value(queryId);
- if (!watch)
- return;
-
- watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive);
- } else if (type == "WATCH_OBJECT_R") {
- int queryId;
- bool ok;
- ds >> queryId >> ok;
-
- QDeclarativeDebugWatch *watch = watched.value(queryId);
- if (!watch)
- return;
-
- watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive);
- } else if (type == "WATCH_EXPR_OBJECT_R") {
- int queryId;
- bool ok;
- ds >> queryId >> ok;
-
- QDeclarativeDebugWatch *watch = watched.value(queryId);
- if (!watch)
- return;
-
- watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive);
- } else if (type == "UPDATE_WATCH") {
- int queryId;
- int debugId;
- QByteArray name;
- QVariant value;
- ds >> queryId >> debugId >> name >> value;
-
- QDeclarativeDebugWatch *watch = watched.value(queryId, 0);
- if (!watch)
- return;
- emit watch->valueChanged(name, value);
- } else if (type == "OBJECT_CREATED") {
- emit q_ptr->newObjects();
- }
-}
-
-QDeclarativeEngineDebug::QDeclarativeEngineDebug(QDeclarativeDebugConnection *client, QObject *parent)
- : QObject(parent), d_ptr(new QDeclarativeEngineDebugPrivate(this, client))
-{
- setObjectName(d_ptr->client->name());
-}
-
-QDeclarativeEngineDebug::~QDeclarativeEngineDebug()
-{
-}
-
-QDeclarativeDebugClient::Status QDeclarativeEngineDebug::status() const
-{
- Q_D(const QDeclarativeEngineDebug);
-
- return d->client->status();
-}
-
-QDeclarativeDebugPropertyWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugPropertyReference &property, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugPropertyWatch *watch = new QDeclarativeDebugPropertyWatch(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled) {
- int queryId = d->getId();
- watch->m_queryId = queryId;
- watch->m_client = this;
- watch->m_objectDebugId = property.objectDebugId();
- watch->m_name = property.name();
- d->watched.insert(queryId, watch);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("WATCH_PROPERTY") << queryId << property.objectDebugId() << property.name().toUtf8();
- d->client->sendMessage(message);
- } else {
- watch->m_state = QDeclarativeDebugWatch::Dead;
- }
-
- return watch;
-}
-
-QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugContextReference &, const QString &, QObject *)
-{
- qWarning("QDeclarativeEngineDebug::addWatch(): Not implemented");
- return 0;
-}
-
-QDeclarativeDebugObjectExpressionWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugObjectReference &object, const QString &expr, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
- QDeclarativeDebugObjectExpressionWatch *watch = new QDeclarativeDebugObjectExpressionWatch(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled) {
- int queryId = d->getId();
- watch->m_queryId = queryId;
- watch->m_client = this;
- watch->m_objectDebugId = object.debugId();
- watch->m_expr = expr;
- d->watched.insert(queryId, watch);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("WATCH_EXPR_OBJECT") << queryId << object.debugId() << expr;
- d->client->sendMessage(message);
- } else {
- watch->m_state = QDeclarativeDebugWatch::Dead;
- }
- return watch;
-}
-
-QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugObjectReference &object, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugWatch *watch = new QDeclarativeDebugWatch(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled) {
- int queryId = d->getId();
- watch->m_queryId = queryId;
- watch->m_client = this;
- watch->m_objectDebugId = object.debugId();
- d->watched.insert(queryId, watch);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("WATCH_OBJECT") << queryId << object.debugId();
- d->client->sendMessage(message);
- } else {
- watch->m_state = QDeclarativeDebugWatch::Dead;
- }
-
- return watch;
-}
-
-QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugFileReference &, QObject *)
-{
- qWarning("QDeclarativeEngineDebug::addWatch(): Not implemented");
- return 0;
-}
-
-void QDeclarativeEngineDebug::removeWatch(QDeclarativeDebugWatch *watch)
-{
- Q_D(QDeclarativeEngineDebug);
-
- if (!watch || !watch->m_client)
- return;
-
- watch->m_client = 0;
- watch->setState(QDeclarativeDebugWatch::Inactive);
-
- d->watched.remove(watch->queryId());
-
- if (d->client && d->client->status() == QDeclarativeDebugClient::Enabled) {
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("NO_WATCH") << watch->queryId();
- d->client->sendMessage(message);
- }
-}
-
-QDeclarativeDebugEnginesQuery *QDeclarativeEngineDebug::queryAvailableEngines(QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugEnginesQuery *query = new QDeclarativeDebugEnginesQuery(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled) {
- query->m_client = this;
- int queryId = d->getId();
- query->m_queryId = queryId;
- d->enginesQuery.insert(queryId, query);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("LIST_ENGINES") << queryId;
- d->client->sendMessage(message);
- } else {
- query->m_state = QDeclarativeDebugQuery::Error;
- }
-
- return query;
-}
-
-QDeclarativeDebugRootContextQuery *QDeclarativeEngineDebug::queryRootContexts(const QDeclarativeDebugEngineReference &engine, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugRootContextQuery *query = new QDeclarativeDebugRootContextQuery(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled && engine.debugId() != -1) {
- query->m_client = this;
- int queryId = d->getId();
- query->m_queryId = queryId;
- d->rootContextQuery.insert(queryId, query);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("LIST_OBJECTS") << queryId << engine.debugId();
- d->client->sendMessage(message);
- } else {
- query->m_state = QDeclarativeDebugQuery::Error;
- }
-
- return query;
-}
-
-QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObject(const QDeclarativeDebugObjectReference &object, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) {
- query->m_client = this;
- int queryId = d->getId();
- query->m_queryId = queryId;
- d->objectQuery.insert(queryId, query);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("FETCH_OBJECT") << queryId << object.debugId()
- << false << true;
- d->client->sendMessage(message);
- } else {
- query->m_state = QDeclarativeDebugQuery::Error;
- }
-
- return query;
-}
-
-QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObjectRecursive(const QDeclarativeDebugObjectReference &object, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) {
- query->m_client = this;
- int queryId = d->getId();
- query->m_queryId = queryId;
- d->objectQuery.insert(queryId, query);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("FETCH_OBJECT") << queryId << object.debugId()
- << true << true;
- d->client->sendMessage(message);
- } else {
- query->m_state = QDeclarativeDebugQuery::Error;
- }
-
- return query;
-}
-
-QDeclarativeDebugExpressionQuery *QDeclarativeEngineDebug::queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent)
-{
- Q_D(QDeclarativeEngineDebug);
-
- QDeclarativeDebugExpressionQuery *query = new QDeclarativeDebugExpressionQuery(parent);
- if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
- query->m_client = this;
- query->m_expr = expr;
- int queryId = d->getId();
- query->m_queryId = queryId;
- d->expressionQuery.insert(queryId, query);
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("EVAL_EXPRESSION") << queryId << objectDebugId << expr;
- d->client->sendMessage(message);
- } else {
- query->m_state = QDeclarativeDebugQuery::Error;
- }
-
- return query;
-}
-
-bool QDeclarativeEngineDebug::setBindingForObject(int objectDebugId, const QString &propertyName,
- const QVariant &bindingExpression,
- bool isLiteralValue,
- QString source, int line)
-{
- Q_D(QDeclarativeEngineDebug);
-
- if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("SET_BINDING") << objectDebugId << propertyName << bindingExpression << isLiteralValue << source << line;
- d->client->sendMessage(message);
- return true;
- } else {
- return false;
- }
-}
-
-bool QDeclarativeEngineDebug::resetBindingForObject(int objectDebugId, const QString &propertyName)
-{
- Q_D(QDeclarativeEngineDebug);
-
- if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("RESET_BINDING") << objectDebugId << propertyName;
- d->client->sendMessage(message);
- return true;
- } else {
- return false;
- }
-}
-
-bool QDeclarativeEngineDebug::setMethodBody(int objectDebugId, const QString &methodName,
- const QString &methodBody)
-{
- Q_D(QDeclarativeEngineDebug);
-
- if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
- ds << QByteArray("SET_METHOD_BODY") << objectDebugId << methodName << methodBody;
- d->client->sendMessage(message);
- return true;
- } else {
- return false;
- }
-}
-
-QDeclarativeDebugWatch::QDeclarativeDebugWatch(QObject *parent)
- : QObject(parent), m_state(Waiting), m_queryId(-1), m_client(0), m_objectDebugId(-1)
-{
-}
-
-QDeclarativeDebugWatch::~QDeclarativeDebugWatch()
-{
- if (m_client && m_queryId != -1)
- QDeclarativeEngineDebugPrivate::remove(m_client, this);
-}
-
-int QDeclarativeDebugWatch::queryId() const
-{
- return m_queryId;
-}
-
-int QDeclarativeDebugWatch::objectDebugId() const
-{
- return m_objectDebugId;
-}
-
-QDeclarativeDebugWatch::State QDeclarativeDebugWatch::state() const
-{
- return m_state;
-}
-
-void QDeclarativeDebugWatch::setState(State s)
-{
- if (m_state == s)
- return;
- m_state = s;
- emit stateChanged(m_state);
-}
-
-QDeclarativeDebugPropertyWatch::QDeclarativeDebugPropertyWatch(QObject *parent)
- : QDeclarativeDebugWatch(parent)
-{
-}
-
-QString QDeclarativeDebugPropertyWatch::name() const
-{
- return m_name;
-}
-
-
-QDeclarativeDebugObjectExpressionWatch::QDeclarativeDebugObjectExpressionWatch(QObject *parent)
- : QDeclarativeDebugWatch(parent)
-{
-}
-
-QString QDeclarativeDebugObjectExpressionWatch::expression() const
-{
- return m_expr;
-}
-
-
-QDeclarativeDebugQuery::QDeclarativeDebugQuery(QObject *parent)
- : QObject(parent), m_state(Waiting)
-{
-}
-
-QDeclarativeDebugQuery::State QDeclarativeDebugQuery::state() const
-{
- return m_state;
-}
-
-bool QDeclarativeDebugQuery::isWaiting() const
-{
- return m_state == Waiting;
-}
-
-void QDeclarativeDebugQuery::setState(State s)
-{
- if (m_state == s)
- return;
- m_state = s;
- emit stateChanged(m_state);
-}
-
-QDeclarativeDebugEnginesQuery::QDeclarativeDebugEnginesQuery(QObject *parent)
- : QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1)
-{
-}
-
-QDeclarativeDebugEnginesQuery::~QDeclarativeDebugEnginesQuery()
-{
- if (m_client && m_queryId != -1)
- QDeclarativeEngineDebugPrivate::remove(m_client, this);
-}
-
-QList<QDeclarativeDebugEngineReference> QDeclarativeDebugEnginesQuery::engines() const
-{
- return m_engines;
-}
-
-QDeclarativeDebugRootContextQuery::QDeclarativeDebugRootContextQuery(QObject *parent)
- : QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1)
-{
-}
-
-QDeclarativeDebugRootContextQuery::~QDeclarativeDebugRootContextQuery()
-{
- if (m_client && m_queryId != -1)
- QDeclarativeEngineDebugPrivate::remove(m_client, this);
-}
-
-QDeclarativeDebugContextReference QDeclarativeDebugRootContextQuery::rootContext() const
-{
- return m_context;
-}
-
-QDeclarativeDebugObjectQuery::QDeclarativeDebugObjectQuery(QObject *parent)
- : QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1)
-{
-}
-
-QDeclarativeDebugObjectQuery::~QDeclarativeDebugObjectQuery()
-{
- if (m_client && m_queryId != -1)
- QDeclarativeEngineDebugPrivate::remove(m_client, this);
-}
-
-QDeclarativeDebugObjectReference QDeclarativeDebugObjectQuery::object() const
-{
- return m_object;
-}
-
-QDeclarativeDebugExpressionQuery::QDeclarativeDebugExpressionQuery(QObject *parent)
- : QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1)
-{
-}
-
-QDeclarativeDebugExpressionQuery::~QDeclarativeDebugExpressionQuery()
-{
- if (m_client && m_queryId != -1)
- QDeclarativeEngineDebugPrivate::remove(m_client, this);
-}
-
-QVariant QDeclarativeDebugExpressionQuery::expression() const
-{
- return m_expr;
-}
-
-QVariant QDeclarativeDebugExpressionQuery::result() const
-{
- return m_result;
-}
-
-QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference()
- : m_debugId(-1)
-{
-}
-
-QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference(int debugId)
- : m_debugId(debugId)
-{
-}
-
-QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference(const QDeclarativeDebugEngineReference &o)
- : m_debugId(o.m_debugId), m_name(o.m_name)
-{
-}
-
-QDeclarativeDebugEngineReference &
-QDeclarativeDebugEngineReference::operator=(const QDeclarativeDebugEngineReference &o)
-{
- m_debugId = o.m_debugId; m_name = o.m_name;
- return *this;
-}
-
-int QDeclarativeDebugEngineReference::debugId() const
-{
- return m_debugId;
-}
-
-QString QDeclarativeDebugEngineReference::name() const
-{
- return m_name;
-}
-
-QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference()
- : m_debugId(-1), m_contextDebugId(-1)
-{
-}
-
-QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference(int debugId)
- : m_debugId(debugId), m_contextDebugId(-1)
-{
-}
-
-QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference(const QDeclarativeDebugObjectReference &o)
- : m_debugId(o.m_debugId), m_class(o.m_class), m_idString(o.m_idString),
- m_name(o.m_name), m_source(o.m_source), m_contextDebugId(o.m_contextDebugId),
- m_properties(o.m_properties), m_children(o.m_children)
-{
-}
-
-QDeclarativeDebugObjectReference &
-QDeclarativeDebugObjectReference::operator=(const QDeclarativeDebugObjectReference &o)
-{
- m_debugId = o.m_debugId; m_class = o.m_class; m_idString = o.m_idString;
- m_name = o.m_name; m_source = o.m_source; m_contextDebugId = o.m_contextDebugId;
- m_properties = o.m_properties; m_children = o.m_children;
- return *this;
-}
-
-int QDeclarativeDebugObjectReference::debugId() const
-{
- return m_debugId;
-}
-
-QString QDeclarativeDebugObjectReference::className() const
-{
- return m_class;
-}
-
-QString QDeclarativeDebugObjectReference::idString() const
-{
- return m_idString;
-}
-
-QString QDeclarativeDebugObjectReference::name() const
-{
- return m_name;
-}
-
-QDeclarativeDebugFileReference QDeclarativeDebugObjectReference::source() const
-{
- return m_source;
-}
-
-int QDeclarativeDebugObjectReference::contextDebugId() const
-{
- return m_contextDebugId;
-}
-
-QList<QDeclarativeDebugPropertyReference> QDeclarativeDebugObjectReference::properties() const
-{
- return m_properties;
-}
-
-QList<QDeclarativeDebugObjectReference> QDeclarativeDebugObjectReference::children() const
-{
- return m_children;
-}
-
-QDeclarativeDebugContextReference::QDeclarativeDebugContextReference()
- : m_debugId(-1)
-{
-}
-
-QDeclarativeDebugContextReference::QDeclarativeDebugContextReference(const QDeclarativeDebugContextReference &o)
- : m_debugId(o.m_debugId), m_name(o.m_name), m_objects(o.m_objects), m_contexts(o.m_contexts)
-{
-}
-
-QDeclarativeDebugContextReference &QDeclarativeDebugContextReference::operator=(const QDeclarativeDebugContextReference &o)
-{
- m_debugId = o.m_debugId; m_name = o.m_name; m_objects = o.m_objects;
- m_contexts = o.m_contexts;
- return *this;
-}
-
-int QDeclarativeDebugContextReference::debugId() const
-{
- return m_debugId;
-}
-
-QString QDeclarativeDebugContextReference::name() const
-{
- return m_name;
-}
-
-QList<QDeclarativeDebugObjectReference> QDeclarativeDebugContextReference::objects() const
-{
- return m_objects;
-}
-
-QList<QDeclarativeDebugContextReference> QDeclarativeDebugContextReference::contexts() const
-{
- return m_contexts;
-}
-
-QDeclarativeDebugFileReference::QDeclarativeDebugFileReference()
- : m_lineNumber(-1), m_columnNumber(-1)
-{
-}
-
-QDeclarativeDebugFileReference::QDeclarativeDebugFileReference(const QDeclarativeDebugFileReference &o)
- : m_url(o.m_url), m_lineNumber(o.m_lineNumber), m_columnNumber(o.m_columnNumber)
-{
-}
-
-QDeclarativeDebugFileReference &QDeclarativeDebugFileReference::operator=(const QDeclarativeDebugFileReference &o)
-{
- m_url = o.m_url; m_lineNumber = o.m_lineNumber; m_columnNumber = o.m_columnNumber;
- return *this;
-}
-
-QUrl QDeclarativeDebugFileReference::url() const
-{
- return m_url;
-}
-
-void QDeclarativeDebugFileReference::setUrl(const QUrl &u)
-{
- m_url = u;
-}
-
-int QDeclarativeDebugFileReference::lineNumber() const
-{
- return m_lineNumber;
-}
-
-void QDeclarativeDebugFileReference::setLineNumber(int l)
-{
- m_lineNumber = l;
-}
-
-int QDeclarativeDebugFileReference::columnNumber() const
-{
- return m_columnNumber;
-}
-
-void QDeclarativeDebugFileReference::setColumnNumber(int c)
-{
- m_columnNumber = c;
-}
-
-QDeclarativeDebugPropertyReference::QDeclarativeDebugPropertyReference()
- : m_objectDebugId(-1), m_hasNotifySignal(false)
-{
-}
-
-QDeclarativeDebugPropertyReference::QDeclarativeDebugPropertyReference(const QDeclarativeDebugPropertyReference &o)
- : m_objectDebugId(o.m_objectDebugId), m_name(o.m_name), m_value(o.m_value),
- m_valueTypeName(o.m_valueTypeName), m_binding(o.m_binding),
- m_hasNotifySignal(o.m_hasNotifySignal)
-{
-}
-
-QDeclarativeDebugPropertyReference &QDeclarativeDebugPropertyReference::operator=(const QDeclarativeDebugPropertyReference &o)
-{
- m_objectDebugId = o.m_objectDebugId; m_name = o.m_name; m_value = o.m_value;
- m_valueTypeName = o.m_valueTypeName; m_binding = o.m_binding;
- m_hasNotifySignal = o.m_hasNotifySignal;
- return *this;
-}
-
-int QDeclarativeDebugPropertyReference::objectDebugId() const
-{
- return m_objectDebugId;
-}
-
-QString QDeclarativeDebugPropertyReference::name() const
-{
- return m_name;
-}
-
-QString QDeclarativeDebugPropertyReference::valueTypeName() const
-{
- return m_valueTypeName;
-}
-
-QVariant QDeclarativeDebugPropertyReference::value() const
-{
- return m_value;
-}
-
-QString QDeclarativeDebugPropertyReference::binding() const
-{
- return m_binding;
-}
-
-bool QDeclarativeDebugPropertyReference::hasNotifySignal() const
-{
- return m_hasNotifySignal;
-}
-
-} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qdeclarativeenginedebug.h b/src/libs/qmljsdebugclient/qdeclarativeenginedebug.h
deleted file mode 100644
index 01d5ce1b7c..0000000000
--- a/src/libs/qmljsdebugclient/qdeclarativeenginedebug.h
+++ /dev/null
@@ -1,370 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#ifndef QDECLARATIVEENGINEDEBUG_H
-#define QDECLARATIVEENGINEDEBUG_H
-
-#include "qmljsdebugclient_global.h"
-#include "qdeclarativedebugclient.h"
-#include <qobject.h>
-#include <qurl.h>
-#include <qvariant.h>
-
-namespace QmlJsDebugClient {
-
-class QDeclarativeDebugConnection;
-class QDeclarativeDebugWatch;
-class QDeclarativeDebugPropertyWatch;
-class QDeclarativeDebugObjectExpressionWatch;
-class QDeclarativeDebugEnginesQuery;
-class QDeclarativeDebugRootContextQuery;
-class QDeclarativeDebugObjectQuery;
-class QDeclarativeDebugExpressionQuery;
-class QDeclarativeDebugPropertyReference;
-class QDeclarativeDebugContextReference;
-class QDeclarativeDebugObjectReference;
-class QDeclarativeDebugFileReference;
-class QDeclarativeDebugEngineReference;
-class QDeclarativeEngineDebugPrivate;
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeEngineDebug : public QObject
-{
- Q_OBJECT
-public:
- explicit QDeclarativeEngineDebug(QDeclarativeDebugConnection *, QObject * = 0);
- ~QDeclarativeEngineDebug();
-
- QDeclarativeDebugClient::Status status() const;
-
- QDeclarativeDebugPropertyWatch *addWatch(const QDeclarativeDebugPropertyReference &,
- QObject *parent = 0);
- QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugContextReference &, const QString &,
- QObject *parent = 0);
- QDeclarativeDebugObjectExpressionWatch *addWatch(const QDeclarativeDebugObjectReference &, const QString &,
- QObject *parent = 0);
- QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugObjectReference &,
- QObject *parent = 0);
- QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugFileReference &,
- QObject *parent = 0);
-
- void removeWatch(QDeclarativeDebugWatch *watch);
-
- QDeclarativeDebugEnginesQuery *queryAvailableEngines(QObject *parent = 0);
- QDeclarativeDebugRootContextQuery *queryRootContexts(const QDeclarativeDebugEngineReference &,
- QObject *parent = 0);
- QDeclarativeDebugObjectQuery *queryObject(const QDeclarativeDebugObjectReference &,
- QObject *parent = 0);
- QDeclarativeDebugObjectQuery *queryObjectRecursive(const QDeclarativeDebugObjectReference &,
- QObject *parent = 0);
- QDeclarativeDebugExpressionQuery *queryExpressionResult(int objectDebugId,
- const QString &expr,
- QObject *parent = 0);
- bool setBindingForObject(int objectDebugId, const QString &propertyName,
- const QVariant &bindingExpression, bool isLiteralValue,
- QString source = QString(), int line = -1);
- bool resetBindingForObject(int objectDebugId, const QString &propertyName);
- bool setMethodBody(int objectDebugId, const QString &methodName, const QString &methodBody);
-
-Q_SIGNALS:
- void newObjects();
- void statusChanged(QDeclarativeDebugClient::Status status);
-
-private:
- Q_DECLARE_PRIVATE(QDeclarativeEngineDebug)
- QScopedPointer<QDeclarativeEngineDebugPrivate> d_ptr;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugWatch : public QObject
-{
- Q_OBJECT
-public:
- enum State { Waiting, Active, Inactive, Dead };
-
- QDeclarativeDebugWatch(QObject *);
- ~QDeclarativeDebugWatch();
-
- int queryId() const;
- int objectDebugId() const;
- State state() const;
-
-Q_SIGNALS:
- void stateChanged(QDeclarativeDebugWatch::State);
- //void objectChanged(int, const QDeclarativeDebugObjectReference &);
- //void valueChanged(int, const QVariant &);
-
- // Server sends value as string if it is a user-type variant
- void valueChanged(const QByteArray &name, const QVariant &value);
-
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- void setState(State);
- State m_state;
- int m_queryId;
- QDeclarativeEngineDebug *m_client;
- int m_objectDebugId;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugPropertyWatch : public QDeclarativeDebugWatch
-{
- Q_OBJECT
-public:
- QDeclarativeDebugPropertyWatch(QObject *parent);
-
- QString name() const;
-
-private:
- friend class QDeclarativeEngineDebug;
- QString m_name;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugObjectExpressionWatch : public QDeclarativeDebugWatch
-{
- Q_OBJECT
-public:
- QDeclarativeDebugObjectExpressionWatch(QObject *parent);
-
- QString expression() const;
-
-private:
- friend class QDeclarativeEngineDebug;
- QString m_expr;
- int m_debugId;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugQuery : public QObject
-{
- Q_OBJECT
-public:
- enum State { Waiting, Error, Completed };
-
- State state() const;
- bool isWaiting() const;
-
-Q_SIGNALS:
- void stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State);
-
-protected:
- QDeclarativeDebugQuery(QObject *);
-
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- void setState(State);
- State m_state;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugFileReference
-{
-public:
- QDeclarativeDebugFileReference();
- QDeclarativeDebugFileReference(const QDeclarativeDebugFileReference &);
- QDeclarativeDebugFileReference &operator=(const QDeclarativeDebugFileReference &);
-
- QUrl url() const;
- void setUrl(const QUrl &);
- int lineNumber() const;
- void setLineNumber(int);
- int columnNumber() const;
- void setColumnNumber(int);
-
-private:
- friend class QDeclarativeEngineDebugPrivate;
- QUrl m_url;
- int m_lineNumber;
- int m_columnNumber;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugEngineReference
-{
-public:
- QDeclarativeDebugEngineReference();
- QDeclarativeDebugEngineReference(int);
- QDeclarativeDebugEngineReference(const QDeclarativeDebugEngineReference &);
- QDeclarativeDebugEngineReference &operator=(const QDeclarativeDebugEngineReference &);
-
- int debugId() const;
- QString name() const;
-
-private:
- friend class QDeclarativeEngineDebugPrivate;
- int m_debugId;
- QString m_name;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugObjectReference
-{
-public:
- QDeclarativeDebugObjectReference();
- QDeclarativeDebugObjectReference(int);
- QDeclarativeDebugObjectReference(const QDeclarativeDebugObjectReference &);
- QDeclarativeDebugObjectReference &operator=(const QDeclarativeDebugObjectReference &);
-
- int debugId() const;
- QString className() const;
- QString idString() const;
- QString name() const;
-
- QDeclarativeDebugFileReference source() const;
- int contextDebugId() const;
-
- QList<QDeclarativeDebugPropertyReference> properties() const;
- QList<QDeclarativeDebugObjectReference> children() const;
-
-private:
- friend class QDeclarativeEngineDebugPrivate;
- int m_debugId;
- QString m_class;
- QString m_idString;
- QString m_name;
- QDeclarativeDebugFileReference m_source;
- int m_contextDebugId;
- QList<QDeclarativeDebugPropertyReference> m_properties;
- QList<QDeclarativeDebugObjectReference> m_children;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugContextReference
-{
-public:
- QDeclarativeDebugContextReference();
- QDeclarativeDebugContextReference(const QDeclarativeDebugContextReference &);
- QDeclarativeDebugContextReference &operator=(const QDeclarativeDebugContextReference &);
-
- int debugId() const;
- QString name() const;
-
- QList<QDeclarativeDebugObjectReference> objects() const;
- QList<QDeclarativeDebugContextReference> contexts() const;
-
-private:
- friend class QDeclarativeEngineDebugPrivate;
- int m_debugId;
- QString m_name;
- QList<QDeclarativeDebugObjectReference> m_objects;
- QList<QDeclarativeDebugContextReference> m_contexts;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugPropertyReference
-{
-public:
- QDeclarativeDebugPropertyReference();
- QDeclarativeDebugPropertyReference(const QDeclarativeDebugPropertyReference &);
- QDeclarativeDebugPropertyReference &operator=(const QDeclarativeDebugPropertyReference &);
-
- int objectDebugId() const;
- QString name() const;
- QVariant value() const;
- QString valueTypeName() const;
- QString binding() const;
- bool hasNotifySignal() const;
-
-private:
- friend class QDeclarativeEngineDebugPrivate;
- int m_objectDebugId;
- QString m_name;
- QVariant m_value;
- QString m_valueTypeName;
- QString m_binding;
- bool m_hasNotifySignal;
-};
-
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugEnginesQuery : public QDeclarativeDebugQuery
-{
- Q_OBJECT
-public:
- virtual ~QDeclarativeDebugEnginesQuery();
- QList<QDeclarativeDebugEngineReference> engines() const;
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- QDeclarativeDebugEnginesQuery(QObject *);
- QDeclarativeEngineDebug *m_client;
- int m_queryId;
- QList<QDeclarativeDebugEngineReference> m_engines;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugRootContextQuery : public QDeclarativeDebugQuery
-{
- Q_OBJECT
-public:
- virtual ~QDeclarativeDebugRootContextQuery();
- QDeclarativeDebugContextReference rootContext() const;
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- QDeclarativeDebugRootContextQuery(QObject *);
- QDeclarativeEngineDebug *m_client;
- int m_queryId;
- QDeclarativeDebugContextReference m_context;
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugObjectQuery : public QDeclarativeDebugQuery
-{
- Q_OBJECT
-public:
- virtual ~QDeclarativeDebugObjectQuery();
- QDeclarativeDebugObjectReference object() const;
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- QDeclarativeDebugObjectQuery(QObject *);
- QDeclarativeEngineDebug *m_client;
- int m_queryId;
- QDeclarativeDebugObjectReference m_object;
-
-};
-
-class QMLJSDEBUGCLIENT_EXPORT QDeclarativeDebugExpressionQuery : public QDeclarativeDebugQuery
-{
- Q_OBJECT
-public:
- virtual ~QDeclarativeDebugExpressionQuery();
- QVariant expression() const;
- QVariant result() const;
-private:
- friend class QDeclarativeEngineDebug;
- friend class QDeclarativeEngineDebugPrivate;
- QDeclarativeDebugExpressionQuery(QObject *);
- QDeclarativeEngineDebug *m_client;
- int m_queryId;
- QVariant m_expr;
- QVariant m_result;
-};
-
-} // namespace QmlJsDebugClient
-
-Q_DECLARE_METATYPE(QmlJsDebugClient::QDeclarativeDebugEngineReference)
-Q_DECLARE_METATYPE(QmlJsDebugClient::QDeclarativeDebugObjectReference)
-Q_DECLARE_METATYPE(QmlJsDebugClient::QDeclarativeDebugContextReference)
-Q_DECLARE_METATYPE(QmlJsDebugClient::QDeclarativeDebugPropertyReference)
-
-#endif // QDECLARATIVEENGINEDEBUG_H
diff --git a/src/libs/qmljsdebugclient/qmldebuggerclient.cpp b/src/libs/qmljsdebugclient/qmldebuggerclient.cpp
new file mode 100644
index 0000000000..f995e710d1
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qmldebuggerclient.cpp
@@ -0,0 +1,92 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmldebuggerclient.h"
+
+namespace QmlJsDebugClient {
+
+QmlDebuggerClient::QmlDebuggerClient(
+ QDeclarativeDebugConnection *connection)
+ : QmlEngineDebugClient(QLatin1String("QmlDebugger"), connection)
+{
+}
+
+quint32 QmlDebuggerClient::setBindingForObject(
+ int objectDebugId,
+ const QString &propertyName,
+ const QVariant &bindingExpression,
+ bool isLiteralValue,
+ QString source, int line)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("SET_BINDING") << id << objectDebugId << propertyName
+ << bindingExpression << isLiteralValue << source << line;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlDebuggerClient::resetBindingForObject(
+ int objectDebugId,
+ const QString &propertyName)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("RESET_BINDING") << id << objectDebugId << propertyName;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlDebuggerClient::setMethodBody(
+ int objectDebugId, const QString &methodName,
+ const QString &methodBody)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("SET_METHOD_BODY") << id << objectDebugId
+ << methodName << methodBody;
+ sendMessage(message);
+ }
+ return id;
+}
+
+} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmldebuggerclient.h b/src/libs/qmljsdebugclient/qmldebuggerclient.h
new file mode 100644
index 0000000000..9fa453c17f
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qmldebuggerclient.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLDEBUGGERCLIENT_H
+#define QMLDEBUGGERCLIENT_H
+
+#include "qmlenginedebugclient.h"
+
+namespace QmlJsDebugClient {
+
+class QDeclarativeDebugConnection;
+
+class QMLJSDEBUGCLIENT_EXPORT QmlDebuggerClient : public QmlEngineDebugClient
+{
+ Q_OBJECT
+public:
+ explicit QmlDebuggerClient(QDeclarativeDebugConnection *conn);
+
+ quint32 setBindingForObject(int objectDebugId, const QString &propertyName,
+ const QVariant &bindingExpression,
+ bool isLiteralValue,
+ QString source, int line);
+ quint32 resetBindingForObject(int objectDebugId, const QString &propertyName);
+ quint32 setMethodBody(int objectDebugId, const QString &methodName,
+ const QString &methodBody);
+};
+
+} // namespace QmlJsDebugClient
+
+#endif // QMLDEBUGGERCLIENT_H
diff --git a/src/libs/qmljsdebugclient/qmlenginedebugclient.cpp b/src/libs/qmljsdebugclient/qmlenginedebugclient.cpp
new file mode 100644
index 0000000000..2593e4b63e
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qmlenginedebugclient.cpp
@@ -0,0 +1,415 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlenginedebugclient.h"
+
+const float CURRENT_SUPPORTED_VERSION = 2.0;
+
+namespace QmlJsDebugClient {
+
+struct QmlObjectData {
+ QUrl url;
+ int lineNumber;
+ int columnNumber;
+ QString idString;
+ QString objectName;
+ QString objectType;
+ int objectId;
+ int contextId;
+};
+
+QDataStream &operator>>(QDataStream &ds, QmlObjectData &data)
+{
+ ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
+ >> data.objectName >> data.objectType >> data.objectId >> data.contextId;
+ return ds;
+}
+
+struct QmlObjectProperty {
+ enum Type { Unknown, Basic, Object, List, SignalProperty };
+ Type type;
+ QString name;
+ QVariant value;
+ QString valueTypeName;
+ QString binding;
+ bool hasNotifySignal;
+};
+
+QDataStream &operator>>(QDataStream &ds, QmlObjectProperty &data)
+{
+ int type;
+ ds >> type >> data.name >> data.value >> data.valueTypeName
+ >> data.binding >> data.hasNotifySignal;
+ data.type = (QmlObjectProperty::Type)type;
+ return ds;
+}
+
+void QmlEngineDebugClient::decode(QDataStream &ds,
+ QmlDebugObjectReference &o,
+ bool simple)
+{
+ QmlObjectData data;
+ ds >> data;
+ int parentId = -1;
+ if (objectName() == QLatin1String("QmlDebugger") &&
+ serviceVersion() >= CURRENT_SUPPORTED_VERSION )
+ ds >> parentId;
+ o.m_debugId = data.objectId;
+ o.m_className = data.objectType;
+ o.m_idString = data.idString;
+ o.m_name = data.objectName;
+ o.m_source.m_url = data.url;
+ o.m_source.m_lineNumber = data.lineNumber;
+ o.m_source.m_columnNumber = data.columnNumber;
+ o.m_contextDebugId = data.contextId;
+ o.m_needsMoreData = simple;
+ o.m_parentId = parentId;
+
+ if (simple)
+ return;
+
+ int childCount;
+ bool recur;
+ ds >> childCount >> recur;
+
+ for (int ii = 0; ii < childCount; ++ii) {
+ o.m_children.append(QmlDebugObjectReference());
+ decode(ds, o.m_children.last(), !recur);
+ }
+
+ int propCount;
+ ds >> propCount;
+
+ for (int ii = 0; ii < propCount; ++ii) {
+ QmlObjectProperty data;
+ ds >> data;
+ QmlDebugPropertyReference prop;
+ prop.m_objectDebugId = o.m_debugId;
+ prop.m_name = data.name;
+ prop.m_binding = data.binding;
+ prop.m_hasNotifySignal = data.hasNotifySignal;
+ prop.m_valueTypeName = data.valueTypeName;
+ switch (data.type) {
+ case QmlObjectProperty::Basic:
+ case QmlObjectProperty::List:
+ case QmlObjectProperty::SignalProperty:
+ {
+ prop.m_value = data.value;
+ break;
+ }
+ case QmlObjectProperty::Object:
+ {
+ QmlDebugObjectReference obj;
+ obj.m_debugId = prop.m_value.toInt();
+ prop.m_value = qVariantFromValue(obj);
+ break;
+ }
+ case QmlObjectProperty::Unknown:
+ break;
+ }
+ o.m_properties << prop;
+ }
+}
+
+void QmlEngineDebugClient::decode(QDataStream &ds,
+ QmlDebugContextReference &c)
+{
+ ds >> c.m_name >> c.m_debugId;
+
+ int contextCount;
+ ds >> contextCount;
+
+ for (int ii = 0; ii < contextCount; ++ii) {
+ c.m_contexts.append(QmlDebugContextReference());
+ decode(ds, c.m_contexts.last());
+ }
+
+ int objectCount;
+ ds >> objectCount;
+
+ for (int ii = 0; ii < objectCount; ++ii) {
+ QmlDebugObjectReference obj;
+ decode(ds, obj, true);
+ obj.m_contextDebugId = c.m_debugId;
+ c.m_objects << obj;
+ }
+}
+
+void QmlEngineDebugClient::statusChanged(Status status)
+{
+ emit newStatus(status);
+}
+
+void QmlEngineDebugClient::messageReceived(const QByteArray &data)
+{
+ QDataStream ds(data);
+ int queryId;
+ QByteArray type;
+ ds >> type;
+
+ if (type == "OBJECT_CREATED") {
+ emit newObjects();
+ return;
+ }
+
+ ds >> queryId;
+
+ if (type == "LIST_ENGINES_R") {
+ int count;
+ ds >> count;
+ QmlDebugEngineReferenceList engines;
+ for (int ii = 0; ii < count; ++ii) {
+ QmlDebugEngineReference eng;
+ ds >> eng.m_name;
+ ds >> eng.m_debugId;
+ engines << eng;
+ }
+ emit result(queryId, QVariant::fromValue(engines), type);
+ } else if (type == "LIST_OBJECTS_R") {
+ QmlDebugContextReference rootContext;
+ if (!ds.atEnd())
+ decode(ds, rootContext);
+ emit result(queryId, QVariant::fromValue(rootContext), type);
+ } else if (type == "FETCH_OBJECT_R") {
+ QmlDebugObjectReference object;
+ if (!ds.atEnd())
+ decode(ds, object, false);
+ emit result(queryId, QVariant::fromValue(object), type);
+ } else if (type == "EVAL_EXPRESSION_R") {;
+ QVariant exprResult;
+ ds >> exprResult;
+ emit result(queryId, exprResult, type);
+ } else if (type == "WATCH_PROPERTY_R" ||
+ type == "WATCH_OBJECT_R" ||
+ type == "WATCH_EXPR_OBJECT_R") {
+ bool valid;
+ ds >> valid;
+ emit result(queryId, valid, type);
+ } else if (type == "UPDATE_WATCH") {
+ int debugId;
+ QByteArray name;
+ QVariant value;
+ ds >> debugId >> name >> value;
+ emit valueChanged(debugId, name, value);
+ }
+}
+
+QmlEngineDebugClient::QmlEngineDebugClient(const QString &clientName,
+ QDeclarativeDebugConnection *conn)
+ : QDeclarativeDebugClient(clientName, conn),
+ m_nextId(1)
+{
+ setObjectName(clientName);
+}
+
+quint32 QmlEngineDebugClient::addWatch(const QmlDebugPropertyReference &property)
+{
+ quint32 id;
+ if (status() == QDeclarativeDebugClient::Enabled) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("WATCH_PROPERTY") << id << property.m_objectDebugId
+ << property.m_name.toUtf8();
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::addWatch(const QmlDebugContextReference &/*context*/,
+ const QString &/*id*/)
+{
+ qWarning("QmlEngineDebugClient::addWatch(): Not implemented");
+ return 0;
+}
+
+quint32 QmlEngineDebugClient::addWatch(const QmlDebugObjectReference &object,
+ const QString &expr)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("WATCH_EXPR_OBJECT") << id << object.m_debugId << expr;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::addWatch(const QmlDebugObjectReference &object)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("WATCH_OBJECT") << id << object.m_debugId;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::addWatch(const QmlDebugFileReference &/*file*/)
+{
+ qWarning("QmlEngineDebugClient::addWatch(): Not implemented");
+ return 0;
+}
+
+void QmlEngineDebugClient::removeWatch(quint32 id)
+{
+ if (status() == QDeclarativeDebugClient::Enabled) {
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("NO_WATCH") << id;
+ sendMessage(message);
+ }
+}
+
+quint32 QmlEngineDebugClient::queryAvailableEngines()
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("LIST_ENGINES") << id;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::queryRootContexts(const QmlDebugEngineReference &engine)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && engine.m_debugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("LIST_OBJECTS") << id << engine.m_debugId;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::queryObject(const QmlDebugObjectReference &object)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && object.m_debugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("FETCH_OBJECT") << id << object.m_debugId << false <<
+ true;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::queryObjectRecursive(const QmlDebugObjectReference &object)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && object.m_debugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("FETCH_OBJECT") << id << object.m_debugId << true <<
+ true;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::queryExpressionResult(int objectDebugId,
+ const QString &expr)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::setBindingForObject(
+ int objectDebugId,
+ const QString &propertyName,
+ const QVariant &bindingExpression,
+ bool isLiteralValue,
+ QString source, int line)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("SET_BINDING") << objectDebugId << propertyName
+ << bindingExpression << isLiteralValue << source << line;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::resetBindingForObject(
+ int objectDebugId,
+ const QString &propertyName)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("RESET_BINDING") << objectDebugId << propertyName;
+ sendMessage(message);
+ }
+ return id;
+}
+
+quint32 QmlEngineDebugClient::setMethodBody(
+ int objectDebugId, const QString &methodName,
+ const QString &methodBody)
+{
+ quint32 id = 0;
+ if (status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
+ id = getId();
+ QByteArray message;
+ QDataStream ds(&message, QIODevice::WriteOnly);
+ ds << QByteArray("SET_METHOD_BODY") << objectDebugId
+ << methodName << methodBody;
+ sendMessage(message);
+ }
+ return id;
+}
+
+} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlenginedebugclient.h b/src/libs/qmljsdebugclient/qmlenginedebugclient.h
new file mode 100644
index 0000000000..a2fa425cba
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qmlenginedebugclient.h
@@ -0,0 +1,232 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLENGINEDEBUGCLIENT_H
+#define QMLENGINEDEBUGCLIENT_H
+
+#include "qmljsdebugclient_global.h"
+#include "qdeclarativedebugclient.h"
+#include <qurl.h>
+#include <qvariant.h>
+
+namespace QmlJsDebugClient {
+
+class QDeclarativeDebugConnection;
+class QmlDebugPropertyReference;
+class QmlDebugContextReference;
+class QmlDebugObjectReference;
+class QmlDebugFileReference;
+class QmlDebugEngineReference;
+
+class QMLJSDEBUGCLIENT_EXPORT QmlEngineDebugClient : public QDeclarativeDebugClient
+{
+ Q_OBJECT
+public:
+ QmlEngineDebugClient(const QString &clientName,
+ QDeclarativeDebugConnection *conn);
+
+ quint32 addWatch(const QmlDebugPropertyReference &property);
+ quint32 addWatch(const QmlDebugContextReference &context, const QString &id);
+ quint32 addWatch(const QmlDebugObjectReference &object, const QString &expr);
+ quint32 addWatch(const QmlDebugObjectReference &object);
+ quint32 addWatch(const QmlDebugFileReference &file);
+
+ void removeWatch(quint32 watch);
+
+ quint32 queryAvailableEngines();
+ quint32 queryRootContexts(const QmlDebugEngineReference &context);
+ quint32 queryObject(const QmlDebugObjectReference &object);
+ quint32 queryObjectRecursive(const QmlDebugObjectReference &object);
+ quint32 queryExpressionResult(int objectDebugId,
+ const QString &expr);
+ virtual quint32 setBindingForObject(int objectDebugId, const QString &propertyName,
+ const QVariant &bindingExpression,
+ bool isLiteralValue,
+ QString source, int line);
+ virtual quint32 resetBindingForObject(int objectDebugId,
+ const QString &propertyName);
+ virtual quint32 setMethodBody(int objectDebugId, const QString &methodName,
+ const QString &methodBody);
+
+signals:
+ void newStatus(QDeclarativeDebugClient::Status status);
+ void newObjects();
+ void valueChanged(int debugId, const QByteArray &name,
+ const QVariant &value);
+ void result(quint32 queryId, const QVariant &result, const QByteArray &type);
+
+protected:
+ virtual void statusChanged(Status status);
+ virtual void messageReceived(const QByteArray &);
+
+ quint32 getId() { return m_nextId++; }
+
+ void decode(QDataStream &d, QmlDebugContextReference &context);
+ void decode(QDataStream &d, QmlDebugObjectReference &object, bool simple);
+
+private:
+ quint32 m_nextId;
+};
+
+class QmlDebugFileReference
+{
+public:
+ QmlDebugFileReference() : m_lineNumber(-1), m_columnNumber(-1) {}
+
+ QUrl url() const { return m_url; }
+ int lineNumber() const { return m_lineNumber; }
+ int columnNumber() const { return m_columnNumber; }
+
+private:
+ friend class QmlEngineDebugClient;
+ QUrl m_url;
+ int m_lineNumber;
+ int m_columnNumber;
+};
+
+class QmlDebugEngineReference
+{
+public:
+ QmlDebugEngineReference() : m_debugId(-1) {}
+ QmlDebugEngineReference(int id) : m_debugId(id) {}
+
+ int debugId() const { return m_debugId; }
+ QString name() const { return m_name; }
+
+private:
+ friend class QmlEngineDebugClient;
+ int m_debugId;
+ QString m_name;
+};
+
+typedef QList<QmlDebugEngineReference> QmlDebugEngineReferenceList;
+
+class QmlDebugObjectReference
+{
+public:
+ QmlDebugObjectReference() : m_debugId(-1), m_parentId(-1), m_contextDebugId(-1), m_needsMoreData(false) {}
+ QmlDebugObjectReference(int id) : m_debugId(id), m_parentId(-1), m_contextDebugId(-1), m_needsMoreData(false) {}
+
+ int debugId() const { return m_debugId; }
+ int parentId() const { return m_parentId; }
+ QString className() const { return m_className; }
+ QString idString() const { return m_idString; }
+ QString name() const { return m_name; }
+
+ QmlDebugFileReference source() const { return m_source; }
+ int contextDebugId() const { return m_contextDebugId; }
+ bool needsMoreData() const { return m_needsMoreData; }
+
+ QList<QmlDebugPropertyReference> properties() const { return m_properties; }
+ QList<QmlDebugObjectReference> children() const { return m_children; }
+
+ bool insertObjectInTree(const QmlDebugObjectReference &obj)
+ {
+ for (int i = 0; i < m_children.count(); i++) {
+ if (m_children[i].debugId() == obj.debugId()) {
+ m_children.replace(i, obj);
+ return true;
+ } else {
+ if (m_children[i].insertObjectInTree(obj))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool operator ==(const QmlDebugObjectReference &obj)
+ {
+ return m_debugId == obj.debugId();
+ }
+
+private:
+ friend class QmlEngineDebugClient;
+ int m_debugId;
+ int m_parentId;
+ QString m_className;
+ QString m_idString;
+ QString m_name;
+ QmlDebugFileReference m_source;
+ int m_contextDebugId;
+ bool m_needsMoreData;
+ QList<QmlDebugPropertyReference> m_properties;
+ QList<QmlDebugObjectReference> m_children;
+};
+
+class QmlDebugContextReference
+{
+public:
+ QmlDebugContextReference() : m_debugId(-1) {}
+
+ int debugId() const { return m_debugId; }
+ QString name() const { return m_name; }
+
+ QList<QmlDebugObjectReference> objects() const { return m_objects; }
+ QList<QmlDebugContextReference> contexts() const { return m_contexts; }
+
+private:
+ friend class QmlEngineDebugClient;
+ int m_debugId;
+ QString m_name;
+ QList<QmlDebugObjectReference> m_objects;
+ QList<QmlDebugContextReference> m_contexts;
+};
+
+class QmlDebugPropertyReference
+{
+public:
+ QmlDebugPropertyReference() : m_objectDebugId(-1), m_hasNotifySignal(false) {}
+
+ int debugId() const { return m_objectDebugId; }
+ QString name() const { return m_name; }
+ QVariant value() const { return m_value; }
+ QString valueTypeName() const { return m_valueTypeName; }
+ QString binding() const { return m_binding; }
+ bool hasNotifySignal() const { return m_hasNotifySignal; }
+
+private:
+ friend class QmlEngineDebugClient;
+ int m_objectDebugId;
+ QString m_name;
+ QVariant m_value;
+ QString m_valueTypeName;
+ QString m_binding;
+ bool m_hasNotifySignal;
+};
+
+} // namespace QmlJsDebugClient
+
+Q_DECLARE_METATYPE(QmlJsDebugClient::QmlDebugObjectReference)
+Q_DECLARE_METATYPE(QmlJsDebugClient::QmlDebugEngineReference)
+Q_DECLARE_METATYPE(QmlJsDebugClient::QmlDebugEngineReferenceList)
+Q_DECLARE_METATYPE(QmlJsDebugClient::QmlDebugContextReference)
+
+#endif // QMLENGINEDEBUGCLIENT_H
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
index f15d920bf9..ccc71ec9c8 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
@@ -7,27 +7,29 @@ contains(CONFIG, dll) {
INCLUDEPATH += $$PWD/..
HEADERS += \
+ $$PWD/qmlprofilereventlocation.h \
$$PWD/qdeclarativedebugclient.h \
- $$PWD/qdeclarativeenginedebug.h \
+ $$PWD/qmlenginedebugclient.h \
+ $$PWD/qdeclarativeengineclient.h \
$$PWD/qdeclarativeoutputparser.h \
$$PWD/qmljsdebugclient_global.h \
- $$PWD/qmlprofilereventlist.h \
$$PWD/qmlprofilereventtypes.h \
$$PWD/qmlprofilertraceclient.h \
$$PWD/qpacketprotocol.h \
$$PWD/qv8profilerclient.h \
$$PWD/qmljsdebugclientconstants.h \
- $$PWD/qdebugmessageclient.h
+ $$PWD/qdebugmessageclient.h \
+ $$PWD/qmldebuggerclient.h
SOURCES += \
$$PWD/qdeclarativedebugclient.cpp \
- $$PWD/qdeclarativeenginedebug.cpp \
+ $$PWD/qmlenginedebugclient.cpp \
$$PWD/qdeclarativeoutputparser.cpp \
- $$PWD/qmlprofilereventlist.cpp \
$$PWD/qmlprofilertraceclient.cpp \
$$PWD/qpacketprotocol.cpp \
$$PWD/qv8profilerclient.cpp \
- $$PWD/qdebugmessageclient.cpp
+ $$PWD/qdebugmessageclient.cpp \
+ $$PWD/qmldebuggerclient.cpp
OTHER_FILES += \
$$PWD/qmljsdebugclient.pri \
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient.pro b/src/libs/qmljsdebugclient/qmljsdebugclient.pro
index c1462ecd5e..893ef89d48 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient.pro
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient.pro
@@ -11,6 +11,3 @@ OTHER_FILES += \
qmljsdebugclient.pri \
qmljsdebugclient-lib.pri
-HEADERS += \
- qmlprofilereventlocation.h
-
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient.qbs b/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
index 9ca80f21af..926c4e283c 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
@@ -24,20 +24,22 @@ QtcLibrary {
"qdeclarativeoutputparser.h",
"qmljsdebugclient_global.h",
"qmljsdebugclientconstants.h",
- "qmlprofilereventlist.h",
+ "qmlprofilereventlocation.h",
"qmlprofilertraceclient.cpp",
"qpacketprotocol.cpp",
"qv8profilerclient.cpp",
"qv8profilerclient.h",
"qdeclarativedebugclient.h",
- "qdeclarativeenginedebug.cpp",
- "qdeclarativeenginedebug.h",
+ "qmlenginedebugclient.cpp",
+ "qmlenginedebugclient.h",
"qmlprofilereventtypes.h",
"qmlprofilertraceclient.h",
"qpacketprotocol.h",
"qdebugmessageclient.cpp",
- "qmlprofilereventlist.cpp",
- "qdebugmessageclient.h"
+ "qdebugmessageclient.h",
+ "qdeclarativeengineclient.h",
+ "qmldebuggerclient.h",
+ "qmldebuggerclient.cpp"
]
ProductModule {
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
deleted file mode 100644
index d056f92a4d..0000000000
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
+++ /dev/null
@@ -1,1887 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "qmlprofilereventlist.h"
-
-#include <QUrl>
-#include <QHash>
-#include <QtAlgorithms>
-#include <QString>
-#include <QStringList>
-
-#include <QFile>
-#include <QXmlStreamReader>
-#include <QXmlStreamWriter>
-
-#include <QTimer>
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-
-namespace QmlJsDebugClient {
-
-namespace Constants {
-const char *const TYPE_PAINTING_STR = "Painting";
-const char *const TYPE_COMPILING_STR = "Compiling";
-const char *const TYPE_CREATING_STR = "Creating";
-const char *const TYPE_BINDING_STR = "Binding";
-const char *const TYPE_HANDLINGSIGNAL_STR = "HandlingSignal";
-const char *const PROFILER_FILE_VERSION = "1.02";
-}
-
-#define MIN_LEVEL 1
-
-QmlEventData::QmlEventData()
-{
- eventType = MaximumQmlEventType;
- eventId = -1;
- duration = 0;
- calls = 0;
- minTime = 0;
- maxTime = 0;
- timePerCall = 0;
- percentOfTime = 0;
- medianTime = 0;
- isBindingLoop = false;
-}
-
-QmlEventData::~QmlEventData()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-QmlEventData &QmlEventData::operator=(const QmlEventData &ref)
-{
- if (this == &ref)
- return *this;
-
- displayname = ref.displayname;
- location = ref.location;
- eventHashStr = ref.eventHashStr;
- details = ref.details;
- eventType = ref.eventType;
- duration = ref.duration;
- calls = ref.calls;
- minTime = ref.minTime;
- maxTime = ref.maxTime;
- timePerCall = ref.timePerCall;
- percentOfTime = ref.percentOfTime;
- medianTime = ref.medianTime;
- eventId = ref.eventId;
- isBindingLoop = ref.isBindingLoop;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key, new QmlEventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key, new QmlEventSub(ref.childrenHash.value(key)));
- }
-
- return *this;
-}
-
-QV8EventData::QV8EventData()
-{
- line = -1;
- eventId = -1;
- totalTime = 0;
- selfTime = 0;
- totalPercent = 0;
- selfPercent = 0;
-}
-
-QV8EventData::~QV8EventData()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
-{
- if (this == &ref)
- return *this;
-
- displayName = ref.displayName;
- filename = ref.filename;
- functionName = ref.functionName;
- line = ref.line;
- totalTime = ref.totalTime;
- totalPercent = ref.totalPercent;
- selfTime = ref.selfTime;
- selfPercent = ref.selfPercent;
- eventId = ref.eventId;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key, new QV8EventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key, new QV8EventSub(ref.childrenHash.value(key)));
- }
- return *this;
-}
-
-// endtimedata
-struct QmlEventEndTimeData {
- qint64 endTime;
- int startTimeIndex;
- QmlEventData *description;
-};
-
-// starttimedata
-struct QmlEventStartTimeData {
- qint64 startTime;
- qint64 length;
- qint64 level;
- int endTimeIndex;
- qint64 nestingLevel;
- qint64 nestingDepth;
- QmlEventData *description;
-
- // animation-related data
- int frameRate;
- int animationCount;
-
- int bindingLoopHead;
-};
-
-struct QmlEventTypeCount {
- QList <int> eventIds;
- int nestingCount;
-};
-
-// used by quicksort
-bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
-{
- return t1.endTime < t2.endTime;
-}
-
-bool compareStartTimes(const QmlEventStartTimeData &t1, const QmlEventStartTimeData &t2)
-{
- return t1.startTime < t2.startTime;
-}
-
-bool compareStartIndexes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
-{
- return t1.startTimeIndex < t2.startTimeIndex;
-}
-
-QString qmlEventType(QmlEventType typeEnum)
-{
- switch (typeEnum) {
- case Painting:
- return QLatin1String(Constants::TYPE_PAINTING_STR);
- break;
- case Compiling:
- return QLatin1String(Constants::TYPE_COMPILING_STR);
- break;
- case Creating:
- return QLatin1String(Constants::TYPE_CREATING_STR);
- break;
- case Binding:
- return QLatin1String(Constants::TYPE_BINDING_STR);
- break;
- case HandlingSignal:
- return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
- break;
- default:
- return QString::number((int)typeEnum);
- }
-}
-
-QmlEventType qmlEventType(const QString &typeString)
-{
- if (typeString == QLatin1String(Constants::TYPE_PAINTING_STR)) {
- return Painting;
- } else if (typeString == QLatin1String(Constants::TYPE_COMPILING_STR)) {
- return Compiling;
- } else if (typeString == QLatin1String(Constants::TYPE_CREATING_STR)) {
- return Creating;
- } else if (typeString == QLatin1String(Constants::TYPE_BINDING_STR)) {
- return Binding;
- } else if (typeString == QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR)) {
- return HandlingSignal;
- } else {
- bool isNumber = false;
- int type = typeString.toUInt(&isNumber);
- if (isNumber) {
- return (QmlEventType)type;
- } else {
- return MaximumQmlEventType;
- }
- }
-}
-
-QString getHashStringForQmlEvent(QmlEventLocation location, int eventType)
-{
- return QString("%1:%2:%3:%4").arg(location.filename, QString::number(location.line), QString::number(location.column), QString::number(eventType));
-}
-
-class QmlProfilerEventList::QmlProfilerEventListPrivate
-{
-public:
- QmlProfilerEventListPrivate(QmlProfilerEventList *qq) : q(qq) {}
-
- QmlProfilerEventList *q;
-
- QmlProfilerEventList::State m_state;
-
- // convenience functions
- void clearQmlRootEvent();
- void clearV8RootEvent();
-
- // Stored data
- QmlEventHash m_eventDescriptions;
- QList<QmlEventEndTimeData> m_endTimeSortedList;
- QList<QmlEventStartTimeData> m_startTimeSortedList;
-
- void collectV8Statistics();
- QV8EventDescriptions m_v8EventList;
- QHash<int, QV8EventData *> m_v8parents;
-
- QmlEventData m_qmlRootEvent;
- QV8EventData m_v8RootEvent;
- QString m_rootEventName;
- QString m_rootEventDesc;
-
- QHash<int, QmlEventTypeCount *> m_typeCounts;
-
- qint64 m_traceEndTime;
- qint64 m_traceStartTime;
- qint64 m_qmlMeasuredTime;
- qint64 m_v8MeasuredTime;
-
- QmlEventStartTimeData *m_lastFrameEvent;
- qint64 m_maximumAnimationCount;
- qint64 m_minimumAnimationCount;
-
- // file to load
- QString m_filename;
-};
-
-
-////////////////////////////////////////////////////////////////////////////////////
-
-
-QmlProfilerEventList::QmlProfilerEventList(QObject *parent) :
- QObject(parent), d(new QmlProfilerEventListPrivate(this))
-{
- setObjectName("QmlProfilerEventStatistics");
-
- d->m_state = Empty;
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
- d->m_rootEventName = tr("<program>");
- d->m_rootEventDesc = tr("Main Program");
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-}
-
-QmlProfilerEventList::~QmlProfilerEventList()
-{
- clear();
- delete d;
-}
-
-void QmlProfilerEventList::clear()
-{
- qDeleteAll(d->m_eventDescriptions.values());
- d->m_eventDescriptions.clear();
-
- qDeleteAll(d->m_v8EventList);
- d->m_v8EventList.clear();
-
- d->m_endTimeSortedList.clear();
- d->m_startTimeSortedList.clear();
-
- d->m_v8parents.clear();
-
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
-
- foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values())
- delete typeCount;
- d->m_typeCounts.clear();
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
-
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-
- emit countChanged();
- setState(Empty);
-}
-
-QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
-{
- return d->m_eventDescriptions.values();
-}
-
-QmlEventData *QmlProfilerEventList::eventDescription(int eventId) const
-{
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-QV8EventData *QmlProfilerEventList::v8EventDescription(int eventId) const
-{
- foreach (QV8EventData *event, d->m_v8EventList) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-const QV8EventDescriptions& QmlProfilerEventList::getV8Events() const
-{
- return d->m_v8EventList;
-}
-
-void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 length,
- const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location)
-{
- const QChar colon = QLatin1Char(':');
- QString displayName, eventHashStr, details;
- QmlJsDebugClient::QmlEventLocation eventLocation = location;
-
- setState(AcquiringData);
-
- // generate details string
- if (data.isEmpty())
- details = tr("Source code not available");
- else {
- details = data.join(" ").replace('\n'," ").simplified();
- QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
- bool match = rewrite.exactMatch(details);
- if (match) {
- details = rewrite.cap(1) + ": " + rewrite.cap(3);
- }
- if (details.startsWith(QString("file://")))
- details = details.mid(details.lastIndexOf(QChar('/')) + 1);
- }
-
- // backwards compatibility: "compiling" events don't have a proper location in older
- // version of the protocol, but the filename is passed in the details string
- if (type == QmlJsDebugClient::Compiling && eventLocation.filename.isEmpty()) {
- eventLocation.filename = details;
- eventLocation.line = 1;
- eventLocation.column = 1;
- }
-
- // generate hash
- if (eventLocation.filename.isEmpty()) {
- displayName = tr("<bytecode>");
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- } else {
- const QString filePath = QUrl(eventLocation.filename).path();
- displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(eventLocation.line);
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- }
-
- QmlEventData *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEventData;
- newEvent->displayname = displayName;
- newEvent->location = eventLocation;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- QmlEventEndTimeData endTimeData;
- endTimeData.endTime = startTime + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTimeData startTimeData;
- startTimeData.startTime = startTime;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = -1;
- startTimeData.frameRate = 1e9/length;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- emit countChanged();
-}
-
-void QmlProfilerEventList::addV8Event(int depth, const QString &function, const QString &filename, int lineNumber, double totalTime, double selfTime)
-{
- QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(lineNumber);
- QV8EventData *eventData = 0;
-
- setState(AcquiringData);
-
- // time is given in milliseconds, but internally we store it in microseconds
- totalTime *= 1e6;
- selfTime *= 1e6;
-
- // cumulate information
- // TODO: use hashes
- foreach (QV8EventData *v8event, d->m_v8EventList) {
- if (v8event->displayName == displayName && v8event->functionName == function) {
- eventData = v8event;
- break;
- }
- }
-
- if (!eventData) {
- eventData = new QV8EventData;
- eventData->displayName = displayName;
- eventData->filename = filename;
- eventData->functionName = function;
- eventData->line = lineNumber;
- eventData->totalTime = totalTime;
- eventData->selfTime = selfTime;
- d->m_v8EventList << eventData;
- } else {
- eventData->totalTime += totalTime;
- eventData->selfTime += selfTime;
- }
- d->m_v8parents[depth] = eventData;
-
- QV8EventData *parentEvent = 0;
- if (depth == 0) {
- parentEvent = &d->m_v8RootEvent;
- d->m_v8MeasuredTime += totalTime;
- }
- if (depth > 0 && d->m_v8parents.contains(depth-1)) {
- parentEvent = d->m_v8parents.value(depth-1);
- }
-
- if (parentEvent != 0) {
- if (!eventData->parentHash.contains(parentEvent->displayName)) {
- QV8EventSub *newParentSub = new QV8EventSub(parentEvent);
- newParentSub->totalTime = totalTime;
-
- eventData->parentHash.insert(parentEvent->displayName, newParentSub );
- } else {
- QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->displayName);
- newParentSub->totalTime += totalTime;
- }
-
- if (!parentEvent->childrenHash.contains(eventData->displayName)) {
- QV8EventSub *newChildSub = new QV8EventSub(eventData);
- newChildSub->totalTime = totalTime;
-
- parentEvent->childrenHash.insert(eventData->displayName, newChildSub);
- } else {
- QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->displayName);
- newChildSub->totalTime += totalTime;
- }
- }
-}
-
-void QmlProfilerEventList::addFrameEvent(qint64 time, int framerate, int animationcount)
-{
- QString displayName, eventHashStr, details;
-
- setState(AcquiringData);
-
- details = tr("Animation Timer Update");
- displayName = tr("<Animation Update>");
- eventHashStr = displayName;
-
- QmlEventData *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEventData;
- newEvent->displayname = displayName;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = QmlJsDebugClient::Painting;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- qint64 length = 1e9/framerate;
- // avoid overlap
- if (d->m_lastFrameEvent && d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length >= time) {
- d->m_lastFrameEvent->length = time - 1 - d->m_lastFrameEvent->startTime;
- d->m_endTimeSortedList[d->m_lastFrameEvent->endTimeIndex].endTime = d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length;
- }
-
- QmlEventEndTimeData endTimeData;
- endTimeData.endTime = time + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTimeData startTimeData;
- startTimeData.startTime = time;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = animationcount;
- startTimeData.frameRate = framerate;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- d->m_lastFrameEvent = &d->m_startTimeSortedList.last();
-
- emit countChanged();
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics()
-{
- if (!m_v8EventList.isEmpty()) {
- double totalTimes = m_v8MeasuredTime;
- double selfTimes = 0;
- foreach (QV8EventData *v8event, m_v8EventList) {
- selfTimes += v8event->selfTime;
- }
-
- // prevent divisions by 0
- if (totalTimes == 0)
- totalTimes = 1;
- if (selfTimes == 0)
- selfTimes = 1;
-
- // insert root event in eventlist
- // the +1 ns is to get it on top of the sorted list
- m_v8RootEvent.totalTime = m_v8MeasuredTime + 1;
- m_v8RootEvent.selfTime = 0;
-
- int rootEventIndex = -1;
- for (int ndx = 0; ndx < m_v8EventList.count(); ndx++)
- {
- if (m_v8EventList.at(ndx)->displayName == m_rootEventName) {
- m_v8RootEvent = *m_v8EventList.at(ndx);
- rootEventIndex = ndx;
- break;
- }
- }
- if (rootEventIndex == -1) {
- rootEventIndex = m_v8EventList.count();
- QV8EventData *newRootEvent = new QV8EventData;
- *newRootEvent = m_v8RootEvent;
- m_v8EventList << newRootEvent;
- }
-
- foreach (QV8EventData *v8event, m_v8EventList) {
- v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
- v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
- }
-
- int index = 0;
- foreach (QV8EventData *v8event, m_v8EventList) {
- v8event->eventId = index++;
- }
- m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId;
- }
-}
-
-void QmlProfilerEventList::setTraceEndTime( qint64 time )
-{
- d->m_traceEndTime = time;
-}
-
-void QmlProfilerEventList::setTraceStartTime( qint64 time )
-{
- d->m_traceStartTime = time;
-}
-
-void QmlProfilerEventList::complete()
-{
- if (currentState() == AcquiringData) {
- setState(ProcessingData);
- d->collectV8Statistics();
- postProcess();
- } else {
- setState(Empty);
- }
-
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::clearQmlRootEvent()
-{
- m_qmlRootEvent.displayname = m_rootEventName;
- m_qmlRootEvent.location = QmlEventLocation();
- m_qmlRootEvent.eventHashStr = m_rootEventName;
- m_qmlRootEvent.details = m_rootEventDesc;
- m_qmlRootEvent.eventType = QmlJsDebugClient::Binding;
- m_qmlRootEvent.duration = 0;
- m_qmlRootEvent.calls = 0;
- m_qmlRootEvent.minTime = 0;
- m_qmlRootEvent.maxTime = 0;
- m_qmlRootEvent.timePerCall = 0;
- m_qmlRootEvent.percentOfTime = 0;
- m_qmlRootEvent.medianTime = 0;
- m_qmlRootEvent.eventId = -1;
-
- qDeleteAll(m_qmlRootEvent.parentHash.values());
- qDeleteAll(m_qmlRootEvent.childrenHash.values());
- m_qmlRootEvent.parentHash.clear();
- m_qmlRootEvent.childrenHash.clear();
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::clearV8RootEvent()
-{
- m_v8RootEvent.displayName = m_rootEventName;
- m_v8RootEvent.functionName = m_rootEventDesc;
- m_v8RootEvent.line = -1;
- m_v8RootEvent.totalTime = 0;
- m_v8RootEvent.totalPercent = 0;
- m_v8RootEvent.selfTime = 0;
- m_v8RootEvent.selfPercent = 0;
- m_v8RootEvent.eventId = -1;
-
- qDeleteAll(m_v8RootEvent.parentHash.values());
- qDeleteAll(m_v8RootEvent.childrenHash.values());
- m_v8RootEvent.parentHash.clear();
- m_v8RootEvent.childrenHash.clear();
-}
-
-void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime)
-{
- int index;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
- double totalTime = 0;
-
- // clear existing statistics
- foreach (QmlEventData *eventDescription, d->m_eventDescriptions.values()) {
- eventDescription->calls = 0;
- // maximum possible value
- eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
- eventDescription->maxTime = 0;
- eventDescription->medianTime = 0;
- eventDescription->duration = 0;
- qDeleteAll(eventDescription->parentHash);
- qDeleteAll(eventDescription->childrenHash);
- eventDescription->parentHash.clear();
- eventDescription->childrenHash.clear();
- }
-
- // create root event for statistics & insert into list
- d->clearQmlRootEvent();
- QmlEventData *listedRootEvent = d->m_eventDescriptions.value(d->m_rootEventName);
- if (!listedRootEvent) {
- listedRootEvent = new QmlEventData;
- d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent);
- }
- *listedRootEvent = d->m_qmlRootEvent;
-
- // compute parent-child relationship and call count
- QHash<int, QmlEventData*> lastParent;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEventData *eventDescription = d->m_startTimeSortedList[index].description;
-
- if (d->m_startTimeSortedList[index].startTime > endTime ||
- d->m_startTimeSortedList[index].startTime+d->m_startTimeSortedList[index].length < startTime) {
- continue;
- }
-
- if (eventDescription->eventType == QmlJsDebugClient::Painting) {
- // skip animation/paint events
- continue;
- }
-
- eventDescription->calls++;
- qint64 duration = d->m_startTimeSortedList[index].length;
- eventDescription->duration += duration;
- if (eventDescription->maxTime < duration)
- eventDescription->maxTime = duration;
- if (eventDescription->minTime > duration)
- eventDescription->minTime = duration;
-
- int level = d->m_startTimeSortedList[index].level;
-
- QmlEventData *parentEvent = listedRootEvent;
- if (level > MIN_LEVEL && lastParent.contains(level-1)) {
- parentEvent = lastParent[level-1];
- }
-
- if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
- QmlEventSub *newParentEvent = new QmlEventSub(parentEvent);
- newParentEvent->calls = 1;
- newParentEvent->duration = duration;
-
- eventDescription->parentHash.insert(parentEvent->eventHashStr, newParentEvent);
- } else {
- QmlEventSub *newParentEvent = eventDescription->parentHash.value(parentEvent->eventHashStr);
- newParentEvent->duration += duration;
- newParentEvent->calls++;
- }
-
- if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
- QmlEventSub *newChildEvent = new QmlEventSub(eventDescription);
- newChildEvent->calls = 1;
- newChildEvent->duration = duration;
-
- parentEvent->childrenHash.insert(eventDescription->eventHashStr, newChildEvent);
- } else {
- QmlEventSub *newChildEvent = parentEvent->childrenHash.value(eventDescription->eventHashStr);
- newChildEvent->duration += duration;
- newChildEvent->calls++;
- }
-
- lastParent[level] = eventDescription;
-
- if (level == MIN_LEVEL) {
- totalTime += duration;
- }
- }
-
- // fake rootEvent statistics
- // the +1 nanosecond is to force it to be on top of the sorted list
- listedRootEvent->duration = totalTime+1;
- listedRootEvent->minTime = totalTime+1;
- listedRootEvent->maxTime = totalTime+1;
- listedRootEvent->medianTime = totalTime+1;
- if (totalTime > 0)
- listedRootEvent->calls = 1;
-
- // copy to the global root reference
- d->m_qmlRootEvent = *listedRootEvent;
-
- // compute percentages
- foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
- binding->percentOfTime = binding->duration * 100.0 / totalTime;
- binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
- }
-
- // compute median time
- QHash < QmlEventData* , QList<qint64> > durationLists;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEventData *desc = d->m_startTimeSortedList[index].description;
- qint64 len = d->m_startTimeSortedList[index].length;
- durationLists[desc].append(len);
- }
- QMutableHashIterator < QmlEventData* , QList<qint64> > iter(durationLists);
- while (iter.hasNext()) {
- iter.next();
- if (!iter.value().isEmpty()) {
- qSort(iter.value());
- iter.key()->medianTime = iter.value().at(iter.value().count()/2);
- }
- }
-
- // find binding loops
- findBindingLoops(startTime, endTime);
-}
-
-void QmlProfilerEventList::prepareForDisplay()
-{
- // generate numeric ids
- int ndx = 0;
- foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
- binding->eventId = ndx++;
- }
-
- // collect type counts
- foreach (const QmlEventStartTimeData &eventStartData, d->m_startTimeSortedList) {
- int typeNumber = eventStartData.description->eventType;
- if (!d->m_typeCounts.contains(typeNumber)) {
- d->m_typeCounts[typeNumber] = new QmlEventTypeCount;
- d->m_typeCounts[typeNumber]->nestingCount = 0;
- }
- if (eventStartData.nestingLevel > d->m_typeCounts[typeNumber]->nestingCount) {
- d->m_typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
- }
- if (!d->m_typeCounts[typeNumber]->eventIds.contains(eventStartData.description->eventId))
- d->m_typeCounts[typeNumber]->eventIds << eventStartData.description->eventId;
- }
-}
-
-void QmlProfilerEventList::sortStartTimes()
-{
- if (d->m_startTimeSortedList.count() < 2)
- return;
-
- // assuming startTimes is partially sorted
- // identify blocks of events and sort them with quicksort
- QList<QmlEventStartTimeData>::iterator itFrom = d->m_startTimeSortedList.end() - 2;
- QList<QmlEventStartTimeData>::iterator itTo = d->m_startTimeSortedList.end() - 1;
-
- while (itFrom != d->m_startTimeSortedList.begin() && itTo != d->m_startTimeSortedList.begin()) {
- // find block to sort
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime > itFrom->startTime ) {
- itTo--;
- itFrom = itTo - 1;
- }
-
- // if we're at the end of the list
- if (itFrom == d->m_startTimeSortedList.begin())
- break;
-
- // find block length
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime <= itFrom->startTime )
- itFrom--;
-
- if (itTo->startTime <= itFrom->startTime)
- qSort(itFrom, itTo + 1, compareStartTimes);
- else
- qSort(itFrom + 1, itTo + 1, compareStartTimes);
-
- // move to next block
- itTo = itFrom;
- itFrom = itTo - 1;
- }
-
- // link back the endTimes
- for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void QmlProfilerEventList::sortEndTimes()
-{
- // assuming endTimes is partially sorted
- // identify blocks of events and sort them with quicksort
-
- if (d->m_endTimeSortedList.count() < 2)
- return;
-
- QList<QmlEventEndTimeData>::iterator itFrom = d->m_endTimeSortedList.begin();
- QList<QmlEventEndTimeData>::iterator itTo = d->m_endTimeSortedList.begin() + 1;
-
- while (itTo != d->m_endTimeSortedList.end() && itFrom != d->m_endTimeSortedList.end()) {
- // find block to sort
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
- itFrom++;
- itTo = itFrom+1;
- }
-
- // if we're at the end of the list
- if (itTo == d->m_endTimeSortedList.end())
- break;
-
- // find block length
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length )
- itTo++;
-
- // sort block
- qSort(itFrom, itTo, compareEndTimes);
-
- // move to next block
- itFrom = itTo;
- itTo = itFrom+1;
-
- }
-
- // link back the startTimes
- for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
- d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
-}
-
-void QmlProfilerEventList::findAnimationLimits()
-{
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
- d->m_lastFrameEvent = 0;
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- if (d->m_startTimeSortedList[i].description->eventType == QmlJsDebugClient::Painting &&
- d->m_startTimeSortedList[i].animationCount >= 0) {
- int animationcount = d->m_startTimeSortedList[i].animationCount;
- if (d->m_lastFrameEvent) {
- if (animationcount > d->m_maximumAnimationCount)
- d->m_maximumAnimationCount = animationcount;
- if (animationcount < d->m_minimumAnimationCount)
- d->m_minimumAnimationCount = animationcount;
- } else {
- d->m_maximumAnimationCount = animationcount;
- d->m_minimumAnimationCount = animationcount;
- }
- d->m_lastFrameEvent = &d->m_startTimeSortedList[i];
- }
- }
-}
-
-void QmlProfilerEventList::computeNestingLevels()
-{
- // compute levels
- QHash <int, qint64> endtimesPerLevel;
- QList <int> nestingLevels;
- QList < QHash <int, qint64> > endtimesPerNestingLevel;
- int level = MIN_LEVEL;
- endtimesPerLevel[MIN_LEVEL] = 0;
-
- for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
- nestingLevels << MIN_LEVEL;
- QHash <int, qint64> dummyHash;
- dummyHash[MIN_LEVEL] = 0;
- endtimesPerNestingLevel << dummyHash;
- }
-
- for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
- qint64 st = d->m_startTimeSortedList[i].startTime;
- int type = d->m_startTimeSortedList[i].description->eventType;
-
- if (type == QmlJsDebugClient::Painting) {
- // animation/paint events have level 1 by definition,
- // but are not considered parents of other events for statistical purposes
- d->m_startTimeSortedList[i].level = MIN_LEVEL;
- d->m_startTimeSortedList[i].nestingLevel = MIN_LEVEL;
- continue;
- }
-
- // general level
- if (endtimesPerLevel[level] > st) {
- level++;
- } else {
- while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
- level--;
- }
- endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
-
- // per type
- if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
- nestingLevels[type]++;
- } else {
- while (nestingLevels[type] > MIN_LEVEL &&
- endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
- nestingLevels[type]--;
- }
- endtimesPerNestingLevel[type][nestingLevels[type]] = st + d->m_startTimeSortedList[i].length;
-
- d->m_startTimeSortedList[i].level = level;
- d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
-
- if (level == MIN_LEVEL) {
- d->m_qmlMeasuredTime += d->m_startTimeSortedList[i].length;
- }
- }
-}
-
-void QmlProfilerEventList::computeNestingDepth()
-{
- QHash <int, int> nestingDepth;
- for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
- int type = d->m_endTimeSortedList[i].description->eventType;
- int nestingInType = d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingLevel;
- if (!nestingDepth.contains(type))
- nestingDepth[type] = nestingInType;
- else {
- int nd = nestingDepth[type];
- nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
- }
-
- d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingDepth = nestingDepth[type];
- if (nestingInType == MIN_LEVEL)
- nestingDepth[type] = MIN_LEVEL;
- }
-}
-
-void QmlProfilerEventList::postProcess()
-{
- if (count() != 0) {
- sortStartTimes();
- sortEndTimes();
- findAnimationLimits();
- computeLevels();
- linkEndsToStarts();
- reloadDetails();
- compileStatistics(traceStartTime(), traceEndTime());
- prepareForDisplay();
- setState(Done);
- } else {
- setState(Empty);
- }
-}
-
-void QmlProfilerEventList::linkEndsToStarts()
-{
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void QmlProfilerEventList::computeLevels()
-{
- computeNestingLevels();
- computeNestingDepth();
-}
-
-void QmlProfilerEventList::reloadDetails()
-{
- // request binding/signal details from the AST
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- if (event->eventType != Binding && event->eventType != HandlingSignal)
- continue;
-
- // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
- if (event->location.filename.isEmpty())
- continue;
-
- // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
- if (event->location.column == -1)
- continue;
-
- emit requestDetailsForLocation(event->eventType, event->location);
- }
- emit reloadDocumentsForDetails();
-}
-
-void QmlProfilerEventList::rewriteDetailsString(int eventType, const QmlJsDebugClient::QmlEventLocation &location, const QString &newString)
-{
- QString eventHashStr = getHashStringForQmlEvent(location, eventType);
- QTC_ASSERT(d->m_eventDescriptions.contains(eventHashStr), return);
- d->m_eventDescriptions.value(eventHashStr)->details = newString;
- emit detailsChanged(d->m_eventDescriptions.value(eventHashStr)->eventId, newString);
-}
-
-void QmlProfilerEventList::finishedRewritingDetails()
-{
- emit reloadDetailLabels();
-}
-
-void QmlProfilerEventList::findBindingLoops(qint64 startTime, qint64 endTime)
-{
- // first clear existing data
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- event->isBindingLoop = false;
- foreach (QmlEventSub *parentEvent, event->parentHash.values())
- parentEvent->inLoopPath = false;
- foreach (QmlEventSub *childEvent, event->childrenHash.values())
- childEvent->inLoopPath = false;
- }
-
- QList <QmlEventData *> stackRefs;
- QList <QmlEventStartTimeData *> stack;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- QmlEventData *currentEvent = d->m_startTimeSortedList[i].description;
- QmlEventStartTimeData *inTimeEvent = &d->m_startTimeSortedList[i];
- inTimeEvent->bindingLoopHead = -1;
-
- // managing call stack
- for (int j = stack.count() - 1; j >= 0; j--) {
- if (stack[j]->startTime + stack[j]->length <= inTimeEvent->startTime) {
- stack.removeAt(j);
- stackRefs.removeAt(j);
- }
- }
-
- bool loopDetected = stackRefs.contains(currentEvent);
- stack << inTimeEvent;
- stackRefs << currentEvent;
-
- if (loopDetected) {
- if (i >= fromIndex && i <= toIndex) {
- // for the statistics
- currentEvent->isBindingLoop = true;
- for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
- QmlEventSub *nextEventSub = stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
- nextEventSub->inLoopPath = true;
- QmlEventSub *prevEventSub = stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
- prevEventSub->inLoopPath = true;
- }
- }
-
- // use crossed references to find index in starttimesortedlist
- QmlEventStartTimeData *head = stack[stackRefs.indexOf(currentEvent)];
- inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex;
- d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
- }
- }
-}
-
-// get list of events between A and B:
-// find fist event with endtime after A -> aa
-// find last event with starttime before B -> bb
-// list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
-int QmlProfilerEventList::findFirstIndex(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1)
- {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- // and then go to the parent
- while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
- ndx--;
-
- return ndx;
-}
-
-int QmlProfilerEventList::findFirstIndexNoParents(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1) {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- return ndx;
-}
-
-int QmlProfilerEventList::findLastIndex(qint64 endTime) const
-{
- // in the "starttime" list, find the last event that starts before endtime
- if (d->m_startTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_startTimeSortedList.first().startTime >= endTime)
- return 0; // -1
- if (d->m_startTimeSortedList.length() == 1)
- return 0;
- if (d->m_startTimeSortedList.last().startTime <= endTime)
- return d->m_startTimeSortedList.count()-1;
-
- int fromIndex = 0;
- int toIndex = d->m_startTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_startTimeSortedList[midIndex].startTime < endTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- return fromIndex;
-}
-
-qint64 QmlProfilerEventList::firstTimeMark() const
-{
- if (d->m_startTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_startTimeSortedList[0].startTime;
- }
-}
-
-qint64 QmlProfilerEventList::lastTimeMark() const
-{
- if (d->m_endTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_endTimeSortedList.last().endTime;
- }
-}
-
-qint64 QmlProfilerEventList::traceStartTime() const
-{
- return d->m_traceStartTime != -1? d->m_traceStartTime : firstTimeMark();
-}
-
-qint64 QmlProfilerEventList::traceEndTime() const
-{
- return d->m_traceEndTime ? d->m_traceEndTime : lastTimeMark();
-}
-
-qint64 QmlProfilerEventList::traceDuration() const
-{
- return traceEndTime() - traceStartTime();
-}
-
-qint64 QmlProfilerEventList::qmlMeasuredTime() const
-{
- return d->m_qmlMeasuredTime;
-}
-qint64 QmlProfilerEventList::v8MeasuredTime() const
-{
- return d->m_v8MeasuredTime;
-}
-
-int QmlProfilerEventList::count() const
-{
- return d->m_startTimeSortedList.count();
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-
-bool QmlProfilerEventList::save(const QString &filename)
-{
- if (count() == 0) {
- emit error(tr("No data to save"));
- return false;
- }
-
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- emit error(tr("Could not open %1 for writing").arg(filename));
- return false;
- }
-
- QXmlStreamWriter stream(&file);
- stream.setAutoFormatting(true);
- stream.writeStartDocument();
-
- stream.writeStartElement("trace");
- stream.writeAttribute("version", Constants::PROFILER_FILE_VERSION);
-
- stream.writeAttribute("traceStart", QString::number(traceStartTime()));
- stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
-
- stream.writeStartElement("eventData");
- stream.writeAttribute("totalTime", QString::number(d->m_qmlMeasuredTime));
-
- foreach (const QmlEventData *eventData, d->m_eventDescriptions.values()) {
- stream.writeStartElement("event");
- stream.writeAttribute("index", QString::number(d->m_eventDescriptions.keys().indexOf(eventData->eventHashStr)));
- stream.writeTextElement("displayname", eventData->displayname);
- stream.writeTextElement("type", qmlEventType(eventData->eventType));
- if (!eventData->location.filename.isEmpty()) {
- stream.writeTextElement("filename", eventData->location.filename);
- stream.writeTextElement("line", QString::number(eventData->location.line));
- stream.writeTextElement("column", QString::number(eventData->location.column));
- }
- stream.writeTextElement("details", eventData->details);
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventData
-
- stream.writeStartElement("eventList");
- foreach (const QmlEventStartTimeData &rangedEvent, d->m_startTimeSortedList) {
- stream.writeStartElement("range");
- stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
- stream.writeAttribute("duration", QString::number(rangedEvent.length));
- stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->eventHashStr)));
- if (rangedEvent.description->eventType == QmlJsDebugClient::Painting && rangedEvent.animationCount >= 0) {
- // animation frame
- stream.writeAttribute("framerate", QString::number(rangedEvent.frameRate));
- stream.writeAttribute("animationcount", QString::number(rangedEvent.animationCount));
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventList
-
- stream.writeStartElement("v8profile"); // v8 profiler output
- stream.writeAttribute("totalTime", QString::number(d->m_v8MeasuredTime));
- foreach (QV8EventData *v8event, d->m_v8EventList) {
- stream.writeStartElement("event");
- stream.writeAttribute("index", QString::number(d->m_v8EventList.indexOf(v8event)));
- stream.writeTextElement("displayname", v8event->displayName);
- stream.writeTextElement("functionname", v8event->functionName);
- if (!v8event->filename.isEmpty()) {
- stream.writeTextElement("filename", v8event->filename);
- stream.writeTextElement("line", QString::number(v8event->line));
- }
- stream.writeTextElement("totalTime", QString::number(v8event->totalTime));
- stream.writeTextElement("selfTime", QString::number(v8event->selfTime));
- if (!v8event->childrenHash.isEmpty()) {
- stream.writeStartElement("childrenEvents");
- QStringList childrenIndexes;
- QStringList childrenTimes;
- QStringList parentTimes;
- foreach (QV8EventSub *v8child, v8event->childrenHash.values()) {
- childrenIndexes << QString::number(v8child->reference->eventId);
- childrenTimes << QString::number(v8child->totalTime);
- parentTimes << QString::number(d->m_v8EventList[v8child->reference->eventId]->parentHash[v8event->displayName]->totalTime);
- }
-
- stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
- stream.writeAttribute("childrenTimes", childrenTimes.join(QString(", ")));
- stream.writeAttribute("parentTimes", parentTimes.join(QString(", ")));
- stream.writeEndElement();
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // v8 profiler output
-
- stream.writeEndElement(); // trace
- stream.writeEndDocument();
-
- file.close();
- return true;
-}
-
-void QmlProfilerEventList::setFilename(const QString &filename)
-{
- d->m_filename = filename;
-}
-
-void QmlProfilerEventList::load(const QString &filename)
-{
- setFilename(filename);
- load();
-}
-
-// "be strict in your output but tolerant in your inputs"
-void QmlProfilerEventList::load()
-{
- QString filename = d->m_filename;
-
- QFile file(filename);
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- emit error(tr("Could not open %1 for reading").arg(filename));
- return;
- }
-
- // erase current
- clear();
-
- setState(AcquiringData);
-
- bool readingQmlEvents = false;
- bool readingV8Events = false;
- QHash <int, QmlEventData *> descriptionBuffer;
- QmlEventData *currentEvent = 0;
- QHash <int, QV8EventData *> v8eventBuffer;
- QHash <int, QString> childrenIndexes;
- QHash <int, QString> childrenTimes;
- QHash <int, QString> parentTimes;
- QV8EventData *v8event = 0;
- bool startTimesAreSorted = true;
- bool validVersion = true;
-
- // time computation
- d->m_v8MeasuredTime = 0;
- d->m_qmlMeasuredTime = 0;
- double cumulatedV8Time = 0;
-
- QXmlStreamReader stream(&file);
-
- while (validVersion && !stream.atEnd() && !stream.hasError()) {
- QXmlStreamReader::TokenType token = stream.readNext();
- QString elementName = stream.name().toString();
- switch (token) {
- case QXmlStreamReader::StartDocument : continue;
- case QXmlStreamReader::StartElement : {
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("version"))
- validVersion = attributes.value("version").toString() == Constants::PROFILER_FILE_VERSION;
- else
- validVersion = false;
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
- }
- if (elementName == "eventData" && !readingV8Events) {
- readingQmlEvents = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_qmlMeasuredTime = attributes.value("totalTime").toString().toDouble();
- break;
- }
- if (elementName == "v8profile" && !readingQmlEvents) {
- readingV8Events = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_v8MeasuredTime = attributes.value("totalTime").toString().toDouble();
- break;
- }
-
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
- }
-
- if (elementName == "range") {
- QmlEventStartTimeData rangedEvent;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("startTime"))
- rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
- if (attributes.hasAttribute("duration"))
- rangedEvent.length = attributes.value("duration").toString().toLongLong();
- if (attributes.hasAttribute("framerate"))
- rangedEvent.frameRate = attributes.value("framerate").toString().toInt();
- if (attributes.hasAttribute("animationcount"))
- rangedEvent.animationCount = attributes.value("animationcount").toString().toInt();
- else
- rangedEvent.animationCount = -1;
- if (attributes.hasAttribute("eventIndex")) {
- int ndx = attributes.value("eventIndex").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEventData;
- rangedEvent.description = descriptionBuffer.value(ndx);
- }
- rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
-
- if (!d->m_startTimeSortedList.isEmpty()
- && rangedEvent.startTime < d->m_startTimeSortedList.last().startTime)
- startTimesAreSorted = false;
- d->m_startTimeSortedList << rangedEvent;
-
- QmlEventEndTimeData endTimeEvent;
- endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
- endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
- endTimeEvent.description = rangedEvent.description;
- d->m_endTimeSortedList << endTimeEvent;
- break;
- }
-
- if (readingQmlEvents) {
- if (elementName == "event") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("index")) {
- int ndx = attributes.value("index").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEventData;
- currentEvent = descriptionBuffer[ndx];
- } else {
- currentEvent = 0;
- }
- break;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!currentEvent)
- break;
-
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
- QString readData = stream.text().toString();
-
- if (elementName == "displayname") {
- currentEvent->displayname = readData;
- break;
- }
- if (elementName == "type") {
- currentEvent->eventType = qmlEventType(readData);
- break;
- }
- if (elementName == "filename") {
- currentEvent->location.filename = readData;
- break;
- }
- if (elementName == "line") {
- currentEvent->location.line = readData.toInt();
- break;
- }
- if (elementName == "column") {
- currentEvent->location.column = readData.toInt();
- }
- if (elementName == "details") {
- currentEvent->details = readData;
- break;
- }
- }
-
- if (readingV8Events) {
- if (elementName == "event") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("index")) {
- int ndx = attributes.value("index").toString().toInt();
- if (!v8eventBuffer.value(ndx))
- v8eventBuffer[ndx] = new QV8EventData;
- v8event = v8eventBuffer[ndx];
- } else {
- v8event = 0;
- }
- break;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!v8event)
- break;
-
- if (elementName == "childrenEvents") {
- QXmlStreamAttributes attributes = stream.attributes();
- int eventIndex = v8eventBuffer.key(v8event);
- if (attributes.hasAttribute("list")) {
- // store for later parsing (we haven't read all the events yet)
- childrenIndexes[eventIndex] = attributes.value("list").toString();
- }
- if (attributes.hasAttribute("childrenTimes")) {
- childrenTimes[eventIndex] = attributes.value("childrenTimes").toString();
- }
- if (attributes.hasAttribute("parentTimes")) {
- parentTimes[eventIndex] = attributes.value("parentTimes").toString();
- }
- }
-
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
- QString readData = stream.text().toString();
-
- if (elementName == "displayname") {
- v8event->displayName = readData;
- break;
- }
-
- if (elementName == "functionname") {
- v8event->functionName = readData;
- break;
- }
-
- if (elementName == "filename") {
- v8event->filename = readData;
- break;
- }
-
- if (elementName == "line") {
- v8event->line = readData.toInt();
- break;
- }
-
- if (elementName == "totalTime") {
- v8event->totalTime = readData.toDouble();
- cumulatedV8Time += v8event->totalTime;
- break;
- }
-
- if (elementName == "selfTime") {
- v8event->selfTime = readData.toDouble();
- break;
- }
- }
-
- break;
- }
- case QXmlStreamReader::EndElement : {
- if (elementName == "event") {
- currentEvent = 0;
- break;
- }
- if (elementName == "eventData") {
- readingQmlEvents = false;
- break;
- }
- if (elementName == "v8profile") {
- readingV8Events = false;
- }
- }
- default: break;
- }
- }
-
- file.close();
-
- if (stream.hasError()) {
- emit error(tr("Error while parsing %1").arg(filename));
- clear();
- return;
- }
-
- stream.clear();
-
- if (!validVersion) {
- clear();
- emit error(tr("Invalid version of QML Trace file."));
- return;
- }
-
- // backwards compatibility
- if (d->m_v8MeasuredTime == 0)
- d->m_v8MeasuredTime = cumulatedV8Time;
-
- // move the buffered data to the details cache
- foreach (QmlEventData *desc, descriptionBuffer.values()) {
- desc->eventHashStr = getHashStringForQmlEvent(desc->location, desc->eventType);;
- d->m_eventDescriptions[desc->eventHashStr] = desc;
- }
-
- // sort startTimeSortedList
- if (!startTimesAreSorted) {
- qSort(d->m_startTimeSortedList.begin(), d->m_startTimeSortedList.end(), compareStartTimes);
- for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
- QmlEventStartTimeData startTimeData = d->m_startTimeSortedList[i];
- d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
- }
- qSort(d->m_endTimeSortedList.begin(), d->m_endTimeSortedList.end(), compareStartIndexes);
- }
-
- // find v8events' children and parents
- foreach (int parentIndex, childrenIndexes.keys()) {
- QStringList childrenStrings = childrenIndexes.value(parentIndex).split(QLatin1Char(','));
- QStringList childrenTimesStrings = childrenTimes.value(parentIndex).split(", ");
- QStringList parentTimesStrings = parentTimes.value(parentIndex).split(", ");
- for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
- int childIndex = childrenStrings[ndx].toInt();
- if (v8eventBuffer.value(childIndex)) {
- QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]);
- QV8EventSub *newParent = new QV8EventSub(v8eventBuffer[parentIndex]);
- if (childrenTimesStrings.count() > ndx)
- newChild->totalTime = childrenTimesStrings[ndx].toDouble();
- if (parentTimesStrings.count() > ndx)
- newParent->totalTime = parentTimesStrings[ndx].toDouble();
- v8eventBuffer[parentIndex]->childrenHash.insert(newChild->reference->displayName, newChild);
- v8eventBuffer[childIndex]->parentHash.insert(newParent->reference->displayName, newParent);
- }
- }
- }
- // store v8 events
- d->m_v8EventList = v8eventBuffer.values();
-
- emit countChanged();
-
- descriptionBuffer.clear();
-
- setState(ProcessingData);
- d->collectV8Statistics();
- postProcess();
-}
-
-///////////////////////////////////////////////
-qint64 QmlProfilerEventList::getStartTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime;
-}
-
-qint64 QmlProfilerEventList::getEndTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime + d->m_startTimeSortedList[index].length;
-}
-
-qint64 QmlProfilerEventList::getDuration(int index) const
-{
- return d->m_startTimeSortedList[index].length;
-}
-
-int QmlProfilerEventList::getType(int index) const
-{
- return d->m_startTimeSortedList[index].description->eventType;
-}
-
-int QmlProfilerEventList::getNestingLevel(int index) const
-{
- return d->m_startTimeSortedList[index].nestingLevel;
-}
-
-int QmlProfilerEventList::getNestingDepth(int index) const
-{
- return d->m_startTimeSortedList[index].nestingDepth;
-}
-
-QString QmlProfilerEventList::getFilename(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.filename;
-}
-
-int QmlProfilerEventList::getLine(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.line;
-}
-
-int QmlProfilerEventList::getColumn(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.column;
-}
-
-QString QmlProfilerEventList::getDetails(int index) const
-{
- // special: animations
- if (d->m_startTimeSortedList[index].description->eventType == QmlJsDebugClient::Painting &&
- d->m_startTimeSortedList[index].animationCount >= 0)
- return tr("%1 animations at %2 FPS").arg(
- QString::number(d->m_startTimeSortedList[index].animationCount),
- QString::number(d->m_startTimeSortedList[index].frameRate));
- return d->m_startTimeSortedList[index].description->details;
-}
-
-int QmlProfilerEventList::getEventId(int index) const
-{
- return d->m_startTimeSortedList[index].description->eventId;
-}
-
-int QmlProfilerEventList::getBindingLoopDest(int index) const
-{
- return d->m_startTimeSortedList[index].bindingLoopHead;
-}
-
-int QmlProfilerEventList::getFramerate(int index) const
-{
- return d->m_startTimeSortedList[index].frameRate;
-}
-
-int QmlProfilerEventList::getAnimationCount(int index) const
-{
- return d->m_startTimeSortedList[index].animationCount;
-}
-
-int QmlProfilerEventList::getMaximumAnimationCount() const
-{
- return d->m_maximumAnimationCount;
-}
-
-int QmlProfilerEventList::getMinimumAnimationCount() const
-{
- return d->m_minimumAnimationCount;
-}
-
-int QmlProfilerEventList::uniqueEventsOfType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->eventIds.count();
-}
-
-int QmlProfilerEventList::maxNestingForType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->nestingCount;
-}
-
-QString QmlProfilerEventList::eventTextForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(d->m_typeCounts[type]->eventIds[index])->details;
-}
-
-QString QmlProfilerEventList::eventDisplayNameForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(d->m_typeCounts[type]->eventIds[index])->displayname;
-}
-
-int QmlProfilerEventList::eventIdForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return -1;
- return d->m_typeCounts[type]->eventIds[index];
-}
-
-int QmlProfilerEventList::eventPosInType(int index) const
-{
- int eventType = d->m_startTimeSortedList[index].description->eventType;
- return d->m_typeCounts[eventType]->eventIds.indexOf(d->m_startTimeSortedList[index].description->eventId);
-}
-
-/////////////////////////////////////////
-QmlProfilerEventList::State QmlProfilerEventList::currentState() const
-{
- return d->m_state;
-}
-
-int QmlProfilerEventList::getCurrentStateFromQml() const
-{
- return (int)d->m_state;
-}
-
-void QmlProfilerEventList::setState(QmlProfilerEventList::State state)
-{
- // It's not an error, we are continuously calling "AcquiringData" for example
- if (d->m_state == state)
- return;
-
- switch (state) {
- case Empty:
- // if it's not empty, complain but go on
- QTC_ASSERT(count() == 0, /**/);
- break;
- case AcquiringData:
- // we're not supposed to receive new data while processing older data
- QTC_ASSERT(d->m_state != ProcessingData, return);
- break;
- case ProcessingData:
- QTC_ASSERT(d->m_state == AcquiringData, return);
- break;
- case Done:
- QTC_ASSERT(d->m_state == ProcessingData, return);
- break;
- default:
- qDebug() << "Trying to set unknown state in events list at" << __FILE__ << __LINE__;
- break;
- }
-
- d->m_state = state;
- emit stateChanged();
- return;
-}
-
-} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlocation.h b/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
index 45b7df6ae6..cb317fd2e5 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
@@ -35,6 +35,8 @@
#include "qmljsdebugclient_global.h"
+#include <QString>
+
namespace QmlJsDebugClient {
struct QMLJSDEBUGCLIENT_EXPORT QmlEventLocation
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventtypes.h b/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
index ae1c490002..02e102f039 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
@@ -33,8 +33,6 @@
#ifndef QMLPROFILEREVENTTYPES_H
#define QMLPROFILEREVENTTYPES_H
-#include <QString>
-
namespace QmlJsDebugClient {
enum QmlEventType {
@@ -47,8 +45,15 @@ enum QmlEventType {
MaximumQmlEventType
};
-QString qmlEventType(QmlEventType typeEnum);
-QmlEventType qmlEventType(const QString &typeString);
+namespace Constants {
+const char TYPE_PAINTING_STR[] = "Painting";
+const char TYPE_COMPILING_STR[] = "Compiling";
+const char TYPE_CREATING_STR[] = "Creating";
+const char TYPE_BINDING_STR[] = "Binding";
+const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
+const char PROFILER_FILE_VERSION[] = "1.02";
+const int QML_MIN_LEVEL = 1;
+}
} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
index cc130bba73..01eb3f56b3 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
@@ -159,6 +159,12 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
int event;
stream >> event;
+ // stop with the first data
+ if (d->recording && event != StartTrace)
+ setRecordingFromServer(false);
+ else if ((!d->recording) && event == StartTrace)
+ setRecordingFromServer(true);
+
if (event == EndTrace) {
emit this->traceFinished(time);
d->maximumTime = time;
@@ -169,9 +175,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
emit this->frame(time, frameRate, animationCount);
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == StartTrace) {
- // special: StartTrace is now asynchronous
- if (!d->recording)
- setRecordingFromServer(true);
emit this->traceStarted(time);
d->maximumTime = time;
} else if (event < MaximumEventType) {
@@ -191,6 +194,9 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
++d->rangeCount[range];
+ // stop with the first data
+ if (d->recording)
+ setRecordingFromServer(false);
} else if (messageType == RangeData) {
QString data;
stream >> data;
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
index 4d37c067e3..3ed2071d73 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
@@ -80,10 +80,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
+ void setRecording(bool);
public slots:
- void setRecording(bool);
- void setRecordingFromServer(bool);
void clearData();
void sendRecordingStatus();
@@ -107,6 +106,9 @@ protected:
virtual void messageReceived(const QByteArray &);
private:
+ void setRecordingFromServer(bool);
+
+private:
class QmlProfilerTraceClientPrivate *d;
};
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.cpp b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
index 432ed6c41c..7853efcdb3 100644
--- a/src/libs/qmljsdebugclient/qv8profilerclient.cpp
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
@@ -118,6 +118,16 @@ void QV8ProfilerClient::setRecording(bool v)
emit recordingChanged(v);
}
+void QV8ProfilerClient::setRecordingFromServer(bool v)
+{
+ if (v == d->recording)
+ return;
+
+ d->recording = v;
+
+ emit recordingChanged(v);
+}
+
void QV8ProfilerClient::statusChanged(Status /*status*/)
{
emit enabledChanged();
@@ -133,7 +143,10 @@ void QV8ProfilerClient::messageReceived(const QByteArray &data)
stream >> messageType;
if (messageType == V8Complete) {
+ setRecordingFromServer(false);
emit complete();
+ } else if (messageType == V8ProfilingStarted) {
+ setRecordingFromServer(true);
} else if (messageType == V8Entry) {
QString filename;
QString function;
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.h b/src/libs/qmljsdebugclient/qv8profilerclient.h
index 25e1328c73..48d191a458 100644
--- a/src/libs/qmljsdebugclient/qv8profilerclient.h
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.h
@@ -52,6 +52,9 @@ public:
enum Message {
V8Entry,
V8Complete,
+ V8SnapshotChunk,
+ V8SnapshotComplete,
+ V8ProfilingStarted,
V8MaximumMessage
};
@@ -61,9 +64,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
+ void setRecording(bool);
public slots:
- void setRecording(bool);
void clearData();
void sendRecordingStatus();
@@ -77,6 +80,9 @@ signals:
void enabledChanged();
void cleared();
+private:
+ void setRecordingFromServer(bool);
+
protected:
virtual void statusChanged(Status);
virtual void messageReceived(const QByteArray &);
diff --git a/src/libs/qtcomponents/styleitem/qstyleplugin.cpp b/src/libs/qtcomponents/styleitem/qstyleplugin.cpp
index d615bd4a2f..4eb4427ff3 100644
--- a/src/libs/qtcomponents/styleitem/qstyleplugin.cpp
+++ b/src/libs/qtcomponents/styleitem/qstyleplugin.cpp
@@ -95,4 +95,6 @@ void StylePlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)
engine->addImageProvider("desktoptheme", new DesktopIconProvider);
}
+#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(styleplugin, StylePlugin)
+#endif
diff --git a/src/libs/qtcomponents/styleitem/qstyleplugin.h b/src/libs/qtcomponents/styleitem/qstyleplugin.h
index 74cf097a2e..c10d5a5e02 100644
--- a/src/libs/qtcomponents/styleitem/qstyleplugin.h
+++ b/src/libs/qtcomponents/styleitem/qstyleplugin.h
@@ -50,6 +50,11 @@
class StylePlugin : public QDeclarativeExtensionPlugin
{
Q_OBJECT
+#if QT_VERSION >= 0x050000
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDeclarativeExtensionInterface" FILE "styleitem.json")
+ Q_INTERFACES(QDeclarativeExtensionInterface)
+#endif
+
public:
void registerTypes(const char *uri);
void initializeEngine(QDeclarativeEngine *engine, const char *uri);
diff --git a/src/libs/qtcomponents/styleitem/styleitem.json b/src/libs/qtcomponents/styleitem/styleitem.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/src/libs/qtcomponents/styleitem/styleitem.json
@@ -0,0 +1 @@
+{}
diff --git a/src/libs/symbianutils/symbianutils.qbs b/src/libs/symbianutils/symbianutils.qbs
index f9fadceb84..a586dc8a28 100644
--- a/src/libs/symbianutils/symbianutils.qbs
+++ b/src/libs/symbianutils/symbianutils.qbs
@@ -13,6 +13,8 @@ QtcLibrary {
cpp.defines: [
"SYMBIANUTILS_BUILD_LIB",
"JSON_BUILD_LIB",
+ "JSON_INCLUDE_PRI",
+ "JSON_BUILD_LIB",
"HAS_SERIALPORT"
]
cpp.optimization: "fast"
diff --git a/src/plugins/coreplugin/eventfilteringmainwindow.cpp b/src/libs/utils/appmainwindow.cpp
index b4a786b0ec..09bb333623 100644
--- a/src/plugins/coreplugin/eventfilteringmainwindow.cpp
+++ b/src/libs/utils/appmainwindow.cpp
@@ -30,18 +30,20 @@
**
**************************************************************************/
-#include "eventfilteringmainwindow.h"
+#include "appmainwindow.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
-#include <QtDebug>
#include <QEvent>
#include <QCoreApplication>
-namespace Core {
-namespace Internal {
+#ifdef Q_WS_X11
+#include <QX11Info>
+#endif
+
+namespace Utils {
/* The notification signal is delayed by using a custom event
* as otherwise device removal is not detected properly
@@ -52,13 +54,25 @@ public:
explicit DeviceNotifyEvent(int id) : QEvent(static_cast<QEvent::Type>(id)) {}
};
-EventFilteringMainWindow::EventFilteringMainWindow() :
+AppMainWindow::AppMainWindow() :
m_deviceEventId(QEvent::registerEventType(QEvent::User + 2))
{
}
+void AppMainWindow::raiseWindow()
+{
+ setWindowState(windowState() & ~Qt::WindowMinimized);
+
+ raise();
+#ifdef Q_WS_X11
+ // work around QTBUG-24932
+ QX11Info::setAppUserTime(0);
+#endif
+ activateWindow();
+}
+
#ifdef Q_OS_WIN
-bool EventFilteringMainWindow::event(QEvent *event)
+bool AppMainWindow::event(QEvent *event)
{
if (event->type() == m_deviceEventId) {
event->accept();
@@ -68,7 +82,7 @@ bool EventFilteringMainWindow::event(QEvent *event)
return QMainWindow::event(event);
}
-bool EventFilteringMainWindow::winEvent(MSG *msg, long *result)
+bool AppMainWindow::winEvent(MSG *msg, long *result)
{
if (msg->message == WM_DEVICECHANGE) {
if (msg->wParam & 0x7 /* DBT_DEVNODES_CHANGED */) {
@@ -80,6 +94,5 @@ bool EventFilteringMainWindow::winEvent(MSG *msg, long *result)
}
#endif
-} // namespace Internal
-} // namespace Core
+} // namespace Utils
diff --git a/src/plugins/coreplugin/eventfilteringmainwindow.h b/src/libs/utils/appmainwindow.h
index ae534ecd90..d773bc8551 100644
--- a/src/plugins/coreplugin/eventfilteringmainwindow.h
+++ b/src/libs/utils/appmainwindow.h
@@ -30,28 +30,21 @@
**
**************************************************************************/
-#ifndef EVENTFILTERINGMAINWINDOW_H
-#define EVENTFILTERINGMAINWINDOW_H
+#ifndef APPMAINWINDOW_H
+#define APPMAINWINDOW_H
+#include "utils_global.h"
#include <QMainWindow>
-QT_BEGIN_NAMESPACE
+namespace Utils {
-QT_END_NAMESPACE
-
-namespace Core {
-namespace Internal {
-
-/*!
- * This class only exists because we can't include windows.h in mainwindow.cpp
- * because windows defines an IContext...
- */
-
-class EventFilteringMainWindow : public QMainWindow
+class QTCREATOR_UTILS_EXPORT AppMainWindow : public QMainWindow
{
Q_OBJECT
public:
- EventFilteringMainWindow();
+ AppMainWindow();
+
+ void raiseWindow();
signals:
void deviceChange();
@@ -66,7 +59,6 @@ private:
const int m_deviceEventId;
};
-} // Internal
-} // Core
+} // Utils
-#endif // EVENTFILTERINGMAINWINDOW_H
+#endif // APPMAINWINDOW_H
diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp
new file mode 100644
index 0000000000..b1307bc008
--- /dev/null
+++ b/src/libs/utils/basetreeview.cpp
@@ -0,0 +1,137 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "basetreeview.h"
+
+#include <QHeaderView>
+#include <QMenu>
+#include <QMouseEvent>
+
+namespace Utils {
+
+BaseTreeView::BaseTreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+ setAttribute(Qt::WA_MacShowFocusRect, false);
+ setFrameStyle(QFrame::NoFrame);
+ setRootIsDecorated(false);
+ setIconSize(QSize(10, 10));
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+ setUniformRowHeights(true);
+
+ header()->setDefaultAlignment(Qt::AlignLeft);
+ header()->setClickable(true);
+
+ connect(this, SIGNAL(activated(QModelIndex)),
+ SLOT(rowActivatedHelper(QModelIndex)));
+ connect(header(), SIGNAL(sectionClicked(int)),
+ SLOT(headerSectionClicked(int)));
+
+ m_adjustColumnsAction = new QAction(tr("Adjust Column Widths to Contents"), 0);
+ m_alwaysAdjustColumnsAction = 0;
+}
+
+void BaseTreeView::setAlwaysAdjustColumnsAction(QAction *action)
+{
+ m_alwaysAdjustColumnsAction = action;
+ connect(action, SIGNAL(toggled(bool)),
+ SLOT(setAlwaysResizeColumnsToContents(bool)));
+}
+
+void BaseTreeView::addBaseContextActions(QMenu *menu)
+{
+ menu->addSeparator();
+ if (m_alwaysAdjustColumnsAction)
+ menu->addAction(m_alwaysAdjustColumnsAction);
+ menu->addAction(m_adjustColumnsAction);
+ menu->addSeparator();
+}
+
+bool BaseTreeView::handleBaseContextAction(QAction *act)
+{
+ if (act == 0)
+ return true;
+ if (act == m_adjustColumnsAction) {
+ resizeColumnsToContents();
+ return true;
+ }
+ if (act == m_alwaysAdjustColumnsAction) {
+ if (act->isChecked())
+ resizeColumnsToContents();
+ // Action triggered automatically.
+ return true;
+ }
+ return false;
+}
+
+void BaseTreeView::setModel(QAbstractItemModel *model)
+{
+ QTreeView::setModel(model);
+ if (header() && m_alwaysAdjustColumnsAction)
+ setAlwaysResizeColumnsToContents(m_alwaysAdjustColumnsAction->isChecked());
+}
+
+void BaseTreeView::mousePressEvent(QMouseEvent *ev)
+{
+ QTreeView::mousePressEvent(ev);
+ if (!indexAt(ev->pos()).isValid())
+ resizeColumnsToContents();
+}
+
+void BaseTreeView::resizeColumnsToContents()
+{
+ const int columnCount = model()->columnCount();
+ for (int c = 0 ; c != columnCount; ++c)
+ resizeColumnToContents(c);
+}
+
+void BaseTreeView::setAlwaysResizeColumnsToContents(bool on)
+{
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+}
+
+void BaseTreeView::headerSectionClicked(int logicalIndex)
+{
+ resizeColumnToContents(logicalIndex);
+}
+
+void BaseTreeView::reset()
+{
+ QTreeView::reset();
+ if (header() && m_alwaysAdjustColumnsAction
+ && m_alwaysAdjustColumnsAction->isChecked())
+ resizeColumnsToContents();
+}
+
+} // namespace Utils
diff --git a/src/plugins/qmljsinspector/qmljstoolbarcolorbox.h b/src/libs/utils/basetreeview.h
index 449804d139..c78860ebd0 100644
--- a/src/plugins/qmljsinspector/qmljstoolbarcolorbox.h
+++ b/src/libs/utils/basetreeview.h
@@ -29,47 +29,48 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
-#ifndef TOOLBARCOLORBOX_H
-#define TOOLBARCOLORBOX_H
-#include <QLabel>
-#include <QColor>
-#include <QPoint>
+#ifndef BASETREEVIEW_H
+#define BASETREEVIEW_H
-QT_FORWARD_DECLARE_CLASS(QContextMenuEvent)
-QT_FORWARD_DECLARE_CLASS(QAction)
+#include "utils_global.h"
-namespace QmlJSInspector {
+#include <QTreeView>
-class ToolBarColorBox : public QLabel
+namespace Utils {
+
+class QTCREATOR_UTILS_EXPORT BaseTreeView : public QTreeView
{
Q_OBJECT
+
public:
- explicit ToolBarColorBox(QWidget *parent = 0);
- void setColor(const QColor &color);
- void setInnerBorderColor(const QColor &color);
- void setOuterBorderColor(const QColor &color);
+ BaseTreeView(QWidget *parent = 0);
+
+ void setAlwaysAdjustColumnsAction(QAction *action);
+ virtual void addBaseContextActions(QMenu *menu);
+ bool handleBaseContextAction(QAction *action);
-protected:
- void contextMenuEvent(QContextMenuEvent *ev);
+ void setModel(QAbstractItemModel *model);
+ virtual void rowActivated(const QModelIndex &) {}
void mousePressEvent(QMouseEvent *ev);
- void mouseMoveEvent(QMouseEvent *ev);
-private slots:
- void copyColorToClipboard();
-private:
- QPixmap createDragPixmap(int size = 24) const;
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+ void reset();
-private:
- bool m_dragStarted;
- QPoint m_dragBeginPoint;
- QAction *m_copyHexColorAction;
- QColor m_color;
+protected slots:
+ void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
+
+private slots:
+ void rowActivatedHelper(const QModelIndex &index) { rowActivated(index); }
+ void headerSectionClicked(int logicalIndex);
- QColor m_borderColorOuter;
- QColor m_borderColorInner;
+private:
+ QAction *m_alwaysAdjustColumnsAction;
+ QAction *m_adjustColumnsAction;
};
-} // namespace QmlJSInspector
+} // namespace Utils
-#endif // TOOLBARCOLORBOX_H
+#endif // BASETREEVIEW_H
diff --git a/src/libs/utils/basevalidatinglineedit.cpp b/src/libs/utils/basevalidatinglineedit.cpp
index 375f4a7a32..91cb1132e7 100644
--- a/src/libs/utils/basevalidatinglineedit.cpp
+++ b/src/libs/utils/basevalidatinglineedit.cpp
@@ -167,6 +167,14 @@ void BaseValidatingLineEdit::slotChanged(const QString &t)
emit validChanged();
}
}
+ bool block = blockSignals(true);
+ const QString fixedString = fixInputString(t);
+ if (t != fixedString) {
+ const int cursorPos = cursorPosition();
+ setText(fixedString);
+ setCursorPosition(qMin(cursorPos, fixedString.length()));
+ }
+ blockSignals(block);
}
void BaseValidatingLineEdit::slotReturnPressed()
@@ -180,4 +188,9 @@ void BaseValidatingLineEdit::triggerChanged()
slotChanged(text());
}
+QString BaseValidatingLineEdit::fixInputString(const QString &string)
+{
+ return string;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/basevalidatinglineedit.h b/src/libs/utils/basevalidatinglineedit.h
index 1d9b591ff9..cbf8a00c9d 100644
--- a/src/libs/utils/basevalidatinglineedit.h
+++ b/src/libs/utils/basevalidatinglineedit.h
@@ -77,6 +77,7 @@ signals:
protected:
virtual bool validate(const QString &value, QString *errorMessage) const = 0;
+ virtual QString fixInputString(const QString &string);
protected slots:
// Custom behaviour can be added here. The base implementation must
diff --git a/src/libs/utils/crumblepath.cpp b/src/libs/utils/crumblepath.cpp
index 80c273c97b..069b0f197f 100644
--- a/src/libs/utils/crumblepath.cpp
+++ b/src/libs/utils/crumblepath.cpp
@@ -284,6 +284,11 @@ QVariant CrumblePath::dataForLastIndex() const
return d->m_buttons.last()->data();
}
+int CrumblePath::length() const
+{
+ return d->m_buttons.length();
+}
+
void CrumblePath::pushElement(const QString &title, const QVariant &data)
{
CrumblePathButton *newButton = new CrumblePathButton(title, this);
diff --git a/src/libs/utils/crumblepath.h b/src/libs/utils/crumblepath.h
index c5066c41c8..874adb6405 100644
--- a/src/libs/utils/crumblepath.h
+++ b/src/libs/utils/crumblepath.h
@@ -53,6 +53,7 @@ public:
void selectIndex(int index);
QVariant dataForIndex(int index) const;
QVariant dataForLastIndex() const;
+ int length() const;
public slots:
void pushElement(const QString &title, const QVariant &data = QVariant());
diff --git a/src/libs/utils/filenamevalidatinglineedit.cpp b/src/libs/utils/filenamevalidatinglineedit.cpp
index 8aab1aea73..a90356e190 100644
--- a/src/libs/utils/filenamevalidatinglineedit.cpp
+++ b/src/libs/utils/filenamevalidatinglineedit.cpp
@@ -68,7 +68,8 @@ static const QRegExp &windowsDeviceSubDirPattern()
// ----------- FileNameValidatingLineEdit
FileNameValidatingLineEdit::FileNameValidatingLineEdit(QWidget *parent) :
BaseValidatingLineEdit(parent),
- m_allowDirectories(false)
+ m_allowDirectories(false),
+ m_forceFirstCapitalLetter(false)
{
}
@@ -82,6 +83,16 @@ void FileNameValidatingLineEdit::setAllowDirectories(bool v)
m_allowDirectories = v;
}
+bool FileNameValidatingLineEdit::forceFirstCapitalLetter() const
+{
+ return m_forceFirstCapitalLetter;
+}
+
+void FileNameValidatingLineEdit::setForceFirstCapitalLetter(bool b)
+{
+ m_forceFirstCapitalLetter = b;
+}
+
/* Validate a file base name, check for forbidden characters/strings. */
@@ -144,6 +155,17 @@ bool FileNameValidatingLineEdit::validate(const QString &value, QString *errorM
&& validateFileName(value, allowDirectories(), errorMessage);
}
+QString FileNameValidatingLineEdit::fixInputString(const QString &string)
+{
+ if (!forceFirstCapitalLetter())
+ return string;
+
+ QString fixedString = string;
+ if (!string.isEmpty() && string.at(0).isLower())
+ fixedString[0] = string.at(0).toUpper();
+
+ return fixedString;
+}
bool FileNameValidatingLineEdit::validateFileNameExtension(const QString &fileName,
const QStringList &requiredExtensions,
diff --git a/src/libs/utils/filenamevalidatinglineedit.h b/src/libs/utils/filenamevalidatinglineedit.h
index 582ebbf2e8..7e0e3e4cb3 100644
--- a/src/libs/utils/filenamevalidatinglineedit.h
+++ b/src/libs/utils/filenamevalidatinglineedit.h
@@ -42,6 +42,7 @@ class QTCREATOR_UTILS_EXPORT FileNameValidatingLineEdit : public BaseValidatingL
Q_OBJECT
Q_PROPERTY(bool allowDirectories READ allowDirectories WRITE setAllowDirectories)
Q_PROPERTY(QStringList requiredExtensions READ requiredExtensions WRITE setRequiredExtensions)
+ Q_PROPERTY(bool forceFirstCapitalLetter READ forceFirstCapitalLetter WRITE setForceFirstCapitalLetter)
public:
explicit FileNameValidatingLineEdit(QWidget *parent = 0);
@@ -62,6 +63,13 @@ public:
void setAllowDirectories(bool v);
/**
+ * Sets whether the first letter is forced to be a capital letter
+ * Default is off.
+ */
+ bool forceFirstCapitalLetter() const;
+ void setForceFirstCapitalLetter(bool b);
+
+ /**
* Sets a requred extension. If the extension is empty no extension is required.
* Default is empty.
*/
@@ -70,10 +78,12 @@ public:
protected:
virtual bool validate(const QString &value, QString *errorMessage) const;
+ virtual QString fixInputString(const QString &string);
private:
bool m_allowDirectories;
QStringList m_requiredExtensionList;
+ bool m_forceFirstCapitalLetter;
};
} // namespace Utils
diff --git a/src/libs/utils/filewizarddialog.cpp b/src/libs/utils/filewizarddialog.cpp
index 6cfe526acd..4167221e3c 100644
--- a/src/libs/utils/filewizarddialog.cpp
+++ b/src/libs/utils/filewizarddialog.cpp
@@ -75,6 +75,16 @@ QString FileWizardDialog::path() const
return m_filePage->path();
}
+bool FileWizardDialog::forceFirstCapitalLetterForFileName() const
+{
+ return m_filePage->forceFirstCapitalLetterForFileName();
+}
+
+void FileWizardDialog::setForceFirstCapitalLetterForFileName(bool b)
+{
+ m_filePage->setForceFirstCapitalLetterForFileName(b);
+}
+
void FileWizardDialog::setPath(const QString &path)
{
m_filePage->setPath(path);
diff --git a/src/libs/utils/filewizarddialog.h b/src/libs/utils/filewizarddialog.h
index 0e8ebaf649..5fd0fe6fb2 100644
--- a/src/libs/utils/filewizarddialog.h
+++ b/src/libs/utils/filewizarddialog.h
@@ -49,6 +49,9 @@ public:
QString fileName() const;
QString path() const;
+ bool forceFirstCapitalLetterForFileName() const;
+ void setForceFirstCapitalLetterForFileName(bool b);
+
public slots:
void setPath(const QString &path);
void setFileName(const QString &name);
diff --git a/src/libs/utils/filewizardpage.cpp b/src/libs/utils/filewizardpage.cpp
index 58fd73277d..dde0668a28 100644
--- a/src/libs/utils/filewizardpage.cpp
+++ b/src/libs/utils/filewizardpage.cpp
@@ -50,6 +50,7 @@ struct FileWizardPagePrivate
FileWizardPagePrivate();
Ui::WizardPage m_ui;
bool m_complete;
+ bool m_forceFirstCapitalLetter;
};
FileWizardPagePrivate::FileWizardPagePrivate() :
@@ -121,6 +122,16 @@ void FileWizardPage::setPathLabel(const QString &label)
d->m_ui.pathLabel->setText(label);
}
+bool FileWizardPage::forceFirstCapitalLetterForFileName() const
+{
+ return d->m_ui.nameLineEdit->forceFirstCapitalLetter();
+}
+
+void FileWizardPage::setForceFirstCapitalLetterForFileName(bool b)
+{
+ d->m_ui.nameLineEdit->setForceFirstCapitalLetter(b);
+}
+
void FileWizardPage::slotValidChanged()
{
const bool newComplete = d->m_ui.pathChooser->isValid() && d->m_ui.nameLineEdit->isValid();
diff --git a/src/libs/utils/filewizardpage.h b/src/libs/utils/filewizardpage.h
index e65ee0a01a..ed54c19330 100644
--- a/src/libs/utils/filewizardpage.h
+++ b/src/libs/utils/filewizardpage.h
@@ -59,6 +59,9 @@ public:
void setFileNameLabel(const QString &label);
void setPathLabel(const QString &label);
+ bool forceFirstCapitalLetterForFileName() const;
+ void setForceFirstCapitalLetterForFileName(bool b);
+
// Validate a base name entry field (potentially containing extension)
static bool validateBaseName(const QString &name, QString *errorMessage = 0);
diff --git a/src/libs/utils/iwelcomepage.h b/src/libs/utils/iwelcomepage.h
index 38a973bb7c..67b2de93b5 100644
--- a/src/libs/utils/iwelcomepage.h
+++ b/src/libs/utils/iwelcomepage.h
@@ -54,6 +54,14 @@ class QTCREATOR_UTILS_EXPORT IWelcomePage : public QObject
Q_PROPERTY(bool hasSearchBar READ hasSearchBar CONSTANT)
public:
+ enum Id {
+ GettingStarted = 0,
+ Develop = 1,
+ Examples = 2,
+ Tutorials = 3,
+ UserDefined = 32
+ };
+
IWelcomePage();
virtual ~IWelcomePage();
@@ -62,6 +70,7 @@ public:
virtual int priority() const { return 0; }
virtual void facilitateQml(QDeclarativeEngine *) {}
virtual bool hasSearchBar() const { return false; }
+ virtual Id id() const = 0;
private:
// not used atm
diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index 4a3cf3a723..dfcb3a813d 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -291,6 +291,16 @@ void PathChooser::setBaseDirectory(const QString &directory)
d->m_baseDirectory = directory;
}
+FileName PathChooser::baseFileName() const
+{
+ return Utils::FileName::fromString(d->m_baseDirectory);
+}
+
+void PathChooser::setBaseFileName(const FileName &base)
+{
+ d->m_baseDirectory = base.toString();
+}
+
void PathChooser::setEnvironment(const Utils::Environment &env)
{
QString oldExpand = path();
diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h
index 517fb4644f..cf915346cc 100644
--- a/src/libs/utils/pathchooser.h
+++ b/src/libs/utils/pathchooser.h
@@ -55,12 +55,14 @@ class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget
Q_OBJECT
Q_ENUMS(Kind)
Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true)
- Q_PROPERTY(Utils::FileName fileName READ fileName WRITE setFileName DESIGNABLE true)
Q_PROPERTY(QString promptDialogTitle READ promptDialogTitle WRITE setPromptDialogTitle DESIGNABLE true)
Q_PROPERTY(Kind expectedKind READ expectedKind WRITE setExpectedKind DESIGNABLE true)
Q_PROPERTY(QString baseDirectory READ baseDirectory WRITE setBaseDirectory DESIGNABLE true)
Q_PROPERTY(QStringList commandVersionArguments READ commandVersionArguments WRITE setCommandVersionArguments)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true)
+ // Designer does not know this type, so force designable to false:
+ Q_PROPERTY(Utils::FileName fileName READ fileName WRITE setFileName DESIGNABLE false)
+ Q_PROPERTY(Utils::FileName baseFileName READ baseFileName WRITE setBaseFileName DESIGNABLE false)
public:
static const char * const browseButtonLabel;
@@ -98,6 +100,8 @@ public:
QString baseDirectory() const;
void setBaseDirectory(const QString &directory);
+ Utils::FileName baseFileName() const;
+ void setBaseFileName(const Utils::FileName &base);
void setEnvironment(const Utils::Environment &env);
diff --git a/src/plugins/remotelinux/portlist.cpp b/src/libs/utils/portlist.cpp
index 679f139220..7a3f19c953 100644
--- a/src/plugins/remotelinux/portlist.cpp
+++ b/src/libs/utils/portlist.cpp
@@ -29,6 +29,7 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
+
#include "portlist.h"
#include <QList>
@@ -37,7 +38,7 @@
#include <cctype>
-namespace RemoteLinux {
+namespace Utils {
namespace Internal {
namespace {
@@ -217,4 +218,4 @@ QString PortList::regularExpression()
return QString::fromLatin1("((%1)(,%1)*)?").arg(listElemExpr);
}
-} // namespace RemoteLinux
+} // namespace Utils
diff --git a/src/plugins/remotelinux/portlist.h b/src/libs/utils/portlist.h
index d9681ad80f..967d8d8f5e 100644
--- a/src/plugins/remotelinux/portlist.h
+++ b/src/libs/utils/portlist.h
@@ -29,19 +29,20 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
+
#ifndef PORTLIST_H
#define PORTLIST_H
-#include "remotelinux_export.h"
+#include "utils_global.h"
QT_FORWARD_DECLARE_CLASS(QString)
-namespace RemoteLinux {
+namespace Utils {
namespace Internal {
class PortListPrivate;
} // namespace Internal
-class REMOTELINUX_EXPORT PortList
+class QTCREATOR_UTILS_EXPORT PortList
{
public:
PortList();
@@ -64,6 +65,6 @@ private:
Internal::PortListPrivate * const d;
};
-} // namespace RemoteLinux
+} // namespace Utils
#endif // PORTLIST_H
diff --git a/src/libs/utils/ssh/sftpfilesystemmodel.cpp b/src/libs/utils/ssh/sftpfilesystemmodel.cpp
index a6bfa498e6..d077c698cb 100644
--- a/src/libs/utils/ssh/sftpfilesystemmodel.cpp
+++ b/src/libs/utils/ssh/sftpfilesystemmodel.cpp
@@ -120,8 +120,10 @@ void SftpFileSystemModel::setSshConnection(const SshConnectionParameters &sshPar
d->sshConnection = SshConnectionManager::instance().acquireConnection(sshParams);
connect(d->sshConnection.data(), SIGNAL(error(Utils::SshError)),
SLOT(handleSshConnectionFailure()));
- if (d->sshConnection->state() == SshConnection::Connected)
+ if (d->sshConnection->state() == SshConnection::Connected) {
handleSshConnectionEstablished();
+ return;
+ }
connect(d->sshConnection.data(), SIGNAL(connected()), SLOT(handleSshConnectionEstablished()));
if (d->sshConnection->state() == SshConnection::Unconnected)
d->sshConnection->connectToHost();
diff --git a/src/libs/utils/ssh/sshconnection.cpp b/src/libs/utils/ssh/sshconnection.cpp
index c5111c3d17..a69353801f 100644
--- a/src/libs/utils/ssh/sshconnection.cpp
+++ b/src/libs/utils/ssh/sshconnection.cpp
@@ -84,8 +84,8 @@ namespace {
} // anonymous namespace
-SshConnectionParameters::SshConnectionParameters(ProxyType proxyType) :
- timeout(0), authenticationType(AuthenticationByKey), port(0), proxyType(proxyType)
+SshConnectionParameters::SshConnectionParameters() :
+ timeout(0), authenticationType(AuthenticationByKey), port(0), proxyType(NoProxy)
{
}
diff --git a/src/libs/utils/ssh/sshconnection.h b/src/libs/utils/ssh/sshconnection.h
index 983c6c6890..211502f585 100644
--- a/src/libs/utils/ssh/sshconnection.h
+++ b/src/libs/utils/ssh/sshconnection.h
@@ -56,7 +56,7 @@ class QTCREATOR_UTILS_EXPORT SshConnectionParameters
public:
enum ProxyType { DefaultProxy, NoProxy };
enum AuthenticationType { AuthenticationByPassword, AuthenticationByKey };
- SshConnectionParameters(ProxyType proxyType);
+ SshConnectionParameters();
QString host;
QString userName;
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/libs/utils/ssh/sshkeycreationdialog.cpp
index f0f3f4bb5a..6e53f7e734 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.cpp
+++ b/src/libs/utils/ssh/sshkeycreationdialog.cpp
@@ -31,9 +31,8 @@
#include "sshkeycreationdialog.h"
#include "ui_sshkeycreationdialog.h"
-#include "linuxdeviceconfiguration.h"
+#include "sshkeygenerator.h"
-#include <utils/ssh/sshkeygenerator.h>
#include <utils/fileutils.h>
#include <QDir>
@@ -43,8 +42,7 @@
#include <QDesktopServices>
#include <QMessageBox>
-using namespace Utils;
-using namespace RemoteLinux::Internal;
+namespace Utils {
SshKeyCreationDialog::SshKeyCreationDialog(QWidget *parent)
: QDialog(parent), m_keyGenerator(0), m_ui(new Ui::SshKeyCreationDialog)
@@ -115,7 +113,7 @@ void SshKeyCreationDialog::saveKeys()
return;
QFile::setPermissions(privateKeyFilePath(), QFile::ReadOwner | QFile::WriteOwner);
- FileSaver pubSaver(m_ui->publicKeyFileLabel->text());
+ FileSaver pubSaver(publicKeyFilePath());
pubSaver.write(m_keyGenerator->publicKey());
if (pubSaver.finalize(this))
accept();
@@ -125,3 +123,10 @@ QString SshKeyCreationDialog::privateKeyFilePath() const
{
return m_ui->privateKeyFilePathChooser->path();
}
+
+QString SshKeyCreationDialog::publicKeyFilePath() const
+{
+ return m_ui->publicKeyFileLabel->text();
+}
+
+} // namespace Utils
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.h b/src/libs/utils/ssh/sshkeycreationdialog.h
index 2acb22480e..b8985294a1 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.h
+++ b/src/libs/utils/ssh/sshkeycreationdialog.h
@@ -32,25 +32,25 @@
#ifndef SSHKEYCREATIONDIALOG_H
#define SSHKEYCREATIONDIALOG_H
+#include <utils/utils_global.h>
+
#include <QDialog>
namespace Utils {
class SshKeyGenerator;
-}
-namespace RemoteLinux {
-namespace Internal {
-namespace Ui {
-class SshKeyCreationDialog;
-}
+namespace Ui { class SshKeyCreationDialog; }
-class SshKeyCreationDialog : public QDialog
+class QTCREATOR_UTILS_EXPORT SshKeyCreationDialog : public QDialog
{
Q_OBJECT
public:
SshKeyCreationDialog(QWidget *parent = 0);
~SshKeyCreationDialog();
+ QString privateKeyFilePath() const;
+ QString publicKeyFilePath() const;
+
private slots:
void keyTypeChanged();
void generateKeys();
@@ -58,14 +58,12 @@ private slots:
private:
void saveKeys();
- QString privateKeyFilePath() const;
private:
- Utils::SshKeyGenerator *m_keyGenerator;
+ SshKeyGenerator *m_keyGenerator;
Ui::SshKeyCreationDialog *m_ui;
};
-} // namespace Internal
-} // namespace RemoteLinux
+} // namespace Utils
#endif // SSHKEYCREATIONDIALOG_H
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.ui b/src/libs/utils/ssh/sshkeycreationdialog.ui
index 48befd8d2d..9e20d07f9b 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.ui
+++ b/src/libs/utils/ssh/sshkeycreationdialog.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>RemoteLinux::Internal::SshKeyCreationDialog</class>
- <widget class="QDialog" name="RemoteLinux::Internal::SshKeyCreationDialog">
+ <class>Utils::SshKeyCreationDialog</class>
+ <widget class="QDialog" name="Utils::SshKeyCreationDialog">
<property name="enabled">
<bool>true</bool>
</property>
@@ -234,7 +234,7 @@
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
- <receiver>RemoteLinux::Internal::SshKeyCreationDialog</receiver>
+ <receiver>Utils::SshKeyCreationDialog</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
diff --git a/src/libs/utils/tcpportsgatherer.cpp b/src/libs/utils/tcpportsgatherer.cpp
new file mode 100644
index 0000000000..636150dcb9
--- /dev/null
+++ b/src/libs/utils/tcpportsgatherer.cpp
@@ -0,0 +1,293 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "tcpportsgatherer.h"
+#include "qtcassert.h"
+#include <QFile>
+#include <QStringList>
+#include <QProcess>
+
+#ifdef Q_OS_WIN
+#include <QLibrary>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#endif
+
+#if defined(Q_OS_WIN) && defined(Q_CC_MINGW)
+
+// Missing declarations for MinGW. This requires MinGW with gcc 4.6.
+typedef enum { } MIB_TCP_STATE;
+
+typedef struct _MIB_TCP6ROW {
+ MIB_TCP_STATE State;
+ IN6_ADDR LocalAddr;
+ DWORD dwLocalScopeId;
+ DWORD dwLocalPort;
+ IN6_ADDR RemoteAddr;
+ DWORD dwRemoteScopeId;
+ DWORD dwRemotePort;
+} MIB_TCP6ROW, *PMIB_TCP6ROW;
+
+typedef struct _MIB_TCP6TABLE {
+ DWORD dwNumEntries;
+ MIB_TCP6ROW table[ANY_SIZE];
+} MIB_TCP6TABLE, *PMIB_TCP6TABLE;
+
+#endif // defined(Q_OS_WIN) && defined(Q_CC_MINGW)
+
+namespace Utils {
+namespace Internal {
+
+class TcpPortsGathererPrivate
+{
+public:
+ TcpPortsGathererPrivate(TcpPortsGatherer::ProtocolFlags protocolFlags)
+ : protocolFlags(protocolFlags) {}
+
+ TcpPortsGatherer::ProtocolFlags protocolFlags;
+ PortList usedPorts;
+
+ void updateWin(TcpPortsGatherer::ProtocolFlags protocolFlags);
+ void updateLinux(TcpPortsGatherer::ProtocolFlags protocolFlags);
+ void updateNetstat(TcpPortsGatherer::ProtocolFlags protocolFlags);
+};
+
+#ifdef Q_OS_WIN
+template <typename Table >
+QSet<int> usedTcpPorts(ULONG (__stdcall *Func)(Table*, PULONG, BOOL))
+{
+ Table *table = static_cast<Table*>(malloc(sizeof(Table)));
+ DWORD dwSize = sizeof(Table);
+
+ // get the necessary size into the dwSize variable
+ DWORD dwRetVal = Func(table, &dwSize, false);
+ if (dwRetVal == ERROR_INSUFFICIENT_BUFFER) {
+ free(table);
+ table = static_cast<Table*>(malloc(dwSize));
+ }
+
+ // get the actual data
+ QSet<int> result;
+ dwRetVal = Func(table, &dwSize, false);
+ if (dwRetVal == NO_ERROR) {
+ for (quint32 i = 0; i < table->dwNumEntries; i++) {
+ quint16 port = ntohs(table->table[i].dwLocalPort);
+ if (!result.contains(port))
+ result.insert(port);
+ }
+ } else {
+ qWarning() << "TcpPortsGatherer: GetTcpTable failed with" << dwRetVal;
+ }
+
+ free(table);
+ return result;
+}
+#endif
+
+void TcpPortsGathererPrivate::updateWin(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+#ifdef Q_OS_WIN
+ QSet<int> ports;
+
+ if (protocolFlags & TcpPortsGatherer::IPv4Protocol)
+ ports.unite(usedTcpPorts<MIB_TCPTABLE>(GetTcpTable));
+
+ //Dynamically load symbol for GetTcp6Table for systems that dont have support for IPV6,
+ //eg Windows XP
+ typedef ULONG (__stdcall *GetTcp6TablePtr)(PMIB_TCP6TABLE, PULONG, BOOL);
+ static GetTcp6TablePtr getTcp6TablePtr = 0;
+
+ if (!getTcp6TablePtr)
+ getTcp6TablePtr = (GetTcp6TablePtr)QLibrary::resolve(QLatin1String("Iphlpapi.dll"),
+ "GetTcp6Table");
+
+ if (getTcp6TablePtr && (protocolFlags & TcpPortsGatherer::IPv6Protocol))
+ ports.unite(usedTcpPorts<MIB_TCP6TABLE>(getTcp6TablePtr));
+
+ foreach (int port, ports) {
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ }
+#endif
+ Q_UNUSED(protocolFlags);
+}
+
+void TcpPortsGathererPrivate::updateLinux(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+ QStringList filePaths;
+ if (protocolFlags & TcpPortsGatherer::IPv4Protocol)
+ filePaths.append(QLatin1String("/proc/net/tcp"));
+ if (protocolFlags & TcpPortsGatherer::IPv6Protocol)
+ filePaths.append(QLatin1String("/proc/net/tcp6"));
+
+ foreach (const QString &filePath, filePaths) {
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ qWarning() << "TcpPortsGatherer: Cannot open file"
+ << filePath << ":" << file.errorString();
+ continue;
+ }
+
+ if (file.atEnd()) // read first line describing the output
+ file.readLine();
+
+ static QRegExp pattern(QLatin1String("^\\s*" // start of line, whitespace
+ "\\d+:\\s*" // integer, colon, space
+ "[0-9A-Fa-f]+:" // hexadecimal number (ip), colon
+ "([0-9A-Fa-f]+)" // hexadecimal number (port!)
+ ));
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine();
+ if (pattern.indexIn(line) != -1) {
+ bool isNumber;
+ quint16 port = pattern.cap(1).toUShort(&isNumber, 16);
+ QTC_ASSERT(isNumber, continue);
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ } else {
+ qWarning() << "TcpPortsGatherer: File" << filePath << "has unexpected format.";
+ continue;
+ }
+ }
+ }
+}
+
+// Only works with FreeBSD version of netstat like we have on Mac OS X
+void TcpPortsGathererPrivate::updateNetstat(TcpPortsGatherer::ProtocolFlags protocolFlags)
+{
+ QStringList netstatArgs;
+
+ netstatArgs.append(QLatin1String("-a")); // show also sockets of server processes
+ netstatArgs.append(QLatin1String("-n")); // show network addresses as numbers
+ netstatArgs.append(QLatin1String("-p"));
+ netstatArgs.append(QLatin1String("tcp"));
+ if (protocolFlags != TcpPortsGatherer::AnyIPProcol) {
+ netstatArgs.append(QLatin1String("-f")); // limit to address family
+ if (protocolFlags == TcpPortsGatherer::IPv4Protocol)
+ netstatArgs.append(QLatin1String("inet"));
+ else
+ netstatArgs.append(QLatin1String("inet6"));
+ }
+
+ QProcess netstatProcess;
+ netstatProcess.start(QLatin1String("netstat"), netstatArgs);
+ if (!netstatProcess.waitForFinished(30000)) {
+ qWarning() << "TcpPortsGatherer: netstat did not return in time.";
+ return;
+ }
+
+ QList<QByteArray> output = netstatProcess.readAllStandardOutput().split('\n');
+ foreach (const QByteArray &line, output) {
+ static QRegExp pattern(QLatin1String("^tcp[46]+" // "tcp", followed by "4", "6", "46"
+ "\\s+\\d+" // whitespace, number (Recv-Q)
+ "\\s+\\d+" // whitespace, number (Send-Q)
+ "\\s+(\\S+)")); // whitespace, Local Address
+ if (pattern.indexIn(line) != -1) {
+ QString localAddress = pattern.cap(1);
+
+ // Examples of local addresses:
+ // '*.56501' , '*.*' 'fe80::1%lo0.123'
+ int portDelimiterPos = localAddress.lastIndexOf(".");
+ if (portDelimiterPos == -1)
+ continue;
+
+ localAddress = localAddress.mid(portDelimiterPos + 1);
+ bool isNumber;
+ quint16 port = localAddress.toUShort(&isNumber);
+ if (!isNumber)
+ continue;
+
+ if (!usedPorts.contains(port))
+ usedPorts.addPort(port);
+ }
+ }
+}
+
+} // namespace Internal
+
+
+/*!
+ \class Utils::TcpPortsGatherer
+
+ \brief Gather the list of local TCP ports already in use.
+
+ Query the system for the list of local TCP ports already in use. This information can be used
+ to select a port for use in a range.
+*/
+
+TcpPortsGatherer::TcpPortsGatherer(TcpPortsGatherer::ProtocolFlags protocolFlags)
+ : d(new Internal::TcpPortsGathererPrivate(protocolFlags))
+{
+ update();
+}
+
+TcpPortsGatherer::~TcpPortsGatherer()
+{
+ delete d;
+}
+
+void TcpPortsGatherer::update()
+{
+ d->usedPorts = PortList();
+
+#if defined(Q_OS_WIN)
+ d->updateWin(d->protocolFlags);
+#elif defined(Q_OS_LINUX)
+ d->updateLinux(d->protocolFlags);
+#else
+ d->updateNetstat(d->protocolFlags);
+#endif
+}
+
+PortList TcpPortsGatherer::usedPorts() const
+{
+ return d->usedPorts;
+}
+
+/*!
+ Select a port out of \a freePorts that is not yet used.
+
+ Returns the port, or 0 if no free port is available.
+ */
+quint16 TcpPortsGatherer::getNextFreePort(PortList *freePorts)
+{
+ QTC_ASSERT(freePorts, return 0);
+ while (freePorts->hasMore()) {
+ const int port = freePorts->getNext();
+ if (!d->usedPorts.contains(port))
+ return port;
+ }
+ return 0;
+}
+
+} // namespace Utils
diff --git a/src/tools/qmlprofilertool/main.cpp b/src/libs/utils/tcpportsgatherer.h
index bb5c2bd328..4ad6e24bf2 100644
--- a/src/tools/qmlprofilertool/main.cpp
+++ b/src/libs/utils/tcpportsgatherer.h
@@ -30,26 +30,38 @@
**
**************************************************************************/
-#include "qmlprofilerapplication.h"
-#include "commandlistener.h"
+#ifndef TCPPORTSGATHERER_H
+#define TCPPORTSGATHERER_H
-int main(int argc, char *argv[])
+#include "portlist.h"
+
+namespace Utils {
+namespace Internal {
+class TcpPortsGathererPrivate;
+}
+
+class QTCREATOR_UTILS_EXPORT TcpPortsGatherer
{
- QmlProfilerApplication app(argc, argv);
+public:
+ enum NetworkLayerProtocol {
+ IPv4Protocol = 0x1,
+ IPv6Protocol = 0x2,
+ AnyIPProcol = IPv4Protocol | IPv6Protocol
+ };
+ Q_DECLARE_FLAGS(ProtocolFlags, NetworkLayerProtocol)
- if (!app.parseArguments()) {
- app.printUsage();
- return 1;
- }
+ TcpPortsGatherer(ProtocolFlags flags);
+ ~TcpPortsGatherer();
- CommandListener listener;
- QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString)));
- listener.start();
+ void update();
- int exitValue = app.exec();
- // wait for listener to exit
- listener.wait();
+ PortList usedPorts() const;
+ quint16 getNextFreePort(PortList *port);
+private:
+ Internal::TcpPortsGathererPrivate *d;
+};
- return exitValue;
-}
+} // namespace Utils
+
+#endif // TCPPORTSGATHERER_H
diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri
index b77fbc67d8..6327f3fbdc 100644
--- a/src/libs/utils/utils-lib.pri
+++ b/src/libs/utils/utils-lib.pri
@@ -88,12 +88,17 @@ SOURCES += $$PWD/environment.cpp \
$$PWD/ssh/sshconnectionmanager.cpp \
$$PWD/ssh/sshkeypasswordretriever.cpp \
$$PWD/ssh/sftpfilesystemmodel.cpp \
+ $$PWD/ssh/sshkeycreationdialog.cpp \
$$PWD/outputformatter.cpp \
$$PWD/flowlayout.cpp \
$$PWD/networkaccessmanager.cpp \
$$PWD/persistentsettings.cpp \
$$PWD/completingtextedit.cpp \
- $$PWD/json.cpp
+ $$PWD/json.cpp \
+ $$PWD/portlist.cpp \
+ $$PWD/tcpportsgatherer.cpp \
+ $$PWD/appmainwindow.cpp \
+ $$PWD/basetreeview.cpp
win32 {
SOURCES += \
@@ -194,6 +199,7 @@ HEADERS += \
$$PWD/ssh/sshpseudoterminal.h \
$$PWD/ssh/sshkeypasswordretriever_p.h \
$$PWD/ssh/sftpfilesystemmodel.h \
+ $$PWD/ssh/sshkeycreationdialog.h \
$$PWD/statuslabel.h \
$$PWD/outputformatter.h \
$$PWD/outputformat.h \
@@ -203,11 +209,16 @@ HEADERS += \
$$PWD/completingtextedit.h \
$$PWD/json.h \
$$PWD/multitask.h \
- $$PWD/runextensions.h
+ $$PWD/runextensions.h \
+ $$PWD/portlist.h \
+ $$PWD/tcpportsgatherer.h \
+ $$PWD/appmainwindow.h \
+ $$PWD/basetreeview.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/projectintropage.ui \
$$PWD/newclasswidget.ui \
- $$PWD/submiteditorwidget.ui
+ $$PWD/submiteditorwidget.ui \
+ $$PWD/ssh/sshkeycreationdialog.ui
RESOURCES += $$PWD/utils.qrc
diff --git a/src/libs/utils/utils.pro b/src/libs/utils/utils.pro
index 0a1888c113..8841790040 100644
--- a/src/libs/utils/utils.pro
+++ b/src/libs/utils/utils.pro
@@ -20,3 +20,5 @@ SOURCES += \
proxyaction.cpp
win32: LIBS += -lUser32
+# PortsGatherer
+win32: LIBS += -liphlpapi -lWs2_32
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 31165a7d61..19083fb1b5 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -4,7 +4,7 @@ import "../QtcLibrary.qbs" as QtcLibrary
QtcLibrary {
name: "Utils"
- cpp.defines: [ "QTCREATOR_UTILS_LIB" ]
+ cpp.defines: ["QTCREATOR_UTILS_LIB"]
cpp.includePaths: [ ".", "..",
"../..",
"../3rdparty/botan/build",
@@ -14,7 +14,7 @@ QtcLibrary {
Properties {
condition: qbs.targetOS == "windows"
- cpp.dynamicLibraries: ["user32"]
+ cpp.dynamicLibraries: ["user32", "iphlpapi", "ws2_32"]
}
Depends { name: "cpp" }
@@ -29,6 +29,8 @@ QtcLibrary {
"utils.qrc",
"annotateditemdelegate.cpp",
"annotateditemdelegate.h",
+ "basetreeview.cpp",
+ "basetreeview.h",
"basevalidatinglineedit.cpp",
"basevalidatinglineedit.h",
"buildablehelperlibrary.h",
@@ -57,6 +59,8 @@ QtcLibrary {
"fancylineedit.h",
"fancymainwindow.cpp",
"fancymainwindow.h",
+ "appmainwindow.h",
+ "appmainwindow.cpp",
"fileinprojectfinder.cpp",
"fileinprojectfinder.h",
"filenamevalidatinglineedit.h",
@@ -127,6 +131,8 @@ QtcLibrary {
"submitfieldwidget.h",
"synchronousprocess.cpp",
"synchronousprocess.h",
+ "tcpportsgatherer.cpp",
+ "tcpportsgatherer.h",
"textfileformat.cpp",
"textfileformat.h",
"treewidgetcolumnstretcher.cpp",
@@ -150,6 +156,8 @@ QtcLibrary {
"networkaccessmanager.cpp",
"pathlisteditor.cpp",
"persistentsettings.cpp",
+ "portlist.cpp",
+ "portlist.h",
"proxyaction.cpp",
"qtcprocess.cpp",
"savedaction.cpp",
@@ -215,7 +223,10 @@ QtcLibrary {
"ssh/sshconnection.cpp",
"ssh/sshincomingpacket.cpp",
"ssh/sshkeygenerator.cpp",
- "ssh/sshkeygenerator.h"
+ "ssh/sshkeygenerator.h",
+ "ssh/sshkeycreationdialog.cpp",
+ "ssh/sshkeycreationdialog.h",
+ "ssh/sshkeycreationdialog.ui"
]
Group {
diff --git a/src/libs/zeroconf/avahiLib.cpp b/src/libs/zeroconf/avahiLib.cpp
index 6aeff59c58..0a8912d0fa 100644
--- a/src/libs/zeroconf/avahiLib.cpp
+++ b/src/libs/zeroconf/avahiLib.cpp
@@ -52,6 +52,7 @@
#include <avahi-common/error.h>
#include <netinet/in.h>
+#include <sys/poll.h>
namespace ZeroConf {
namespace Internal {
@@ -70,6 +71,7 @@ extern "C" void cAvahiBrowseReply(
AvahiServiceBrowser * /*b*/, AvahiIfIndex interface, AvahiProtocol /*protocol*/,
AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
AvahiLookupResultFlags /*flags*/, void* context);
+extern "C" int cAvahiPollFunction(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata);
extern "C" {
typedef const AvahiPoll* (*AvahiSimplePollGet)(AvahiSimplePoll *s);
@@ -77,6 +79,7 @@ typedef AvahiSimplePoll *(*AvahiSimplePollNewPtr)(void);
typedef int (*AvahiSimplePollIteratePtr)(AvahiSimplePoll *s, int sleep_time);
typedef void (*AvahiSimplePollQuitPtr)(AvahiSimplePoll *s);
typedef void (*AvahiSimplePollFreePtr)(AvahiSimplePoll *s);
+typedef void (*AvahiSimplePollSetFuncPtr)(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata);
typedef AvahiClient* (*AvahiClientNewPtr)(
const AvahiPoll *poll_api, AvahiClientFlags flags, AvahiClientCallback callback,
void *userdata, int *error);
@@ -113,6 +116,7 @@ private:
AvahiSimplePollIteratePtr m_simplePollIterate;
AvahiSimplePollQuitPtr m_simplePollQuit;
AvahiSimplePollFreePtr m_simplePollFree;
+ AvahiSimplePollSetFuncPtr m_simplePollSetFunc;
AvahiClientNewPtr m_clientNew;
AvahiClientFreePtr m_clientFree;
AvahiServiceBrowserNewPtr m_serviceBrowserNew;
@@ -137,6 +141,7 @@ public:
m_simplePollIterate = reinterpret_cast<AvahiSimplePollIteratePtr>(nativeLib.resolve("avahi_simple_poll_iterate"));
m_simplePollQuit = reinterpret_cast<AvahiSimplePollQuitPtr>(nativeLib.resolve("avahi_simple_poll_quit"));
m_simplePollFree = reinterpret_cast<AvahiSimplePollFreePtr>(nativeLib.resolve("avahi_simple_poll_free"));
+ m_simplePollSetFunc = reinterpret_cast<AvahiSimplePollSetFuncPtr>(nativeLib.resolve("avahi_simple_poll_set_func"));
m_clientNew = reinterpret_cast<AvahiClientNewPtr>(nativeLib.resolve("avahi_client_new"));
m_clientFree = reinterpret_cast<AvahiClientFreePtr>(nativeLib.resolve("avahi_client_free"));
m_serviceBrowserNew = reinterpret_cast<AvahiServiceBrowserNewPtr>(nativeLib.resolve("avahi_service_browser_new"));
@@ -149,6 +154,7 @@ public:
m_simplePollIterate = reinterpret_cast<AvahiSimplePollIteratePtr>(&avahi_simple_poll_iterate);
m_simplePollQuit = reinterpret_cast<AvahiSimplePollQuitPtr>(&avahi_simple_poll_quit);
m_simplePollFree = reinterpret_cast<AvahiSimplePollFreePtr>(&avahi_simple_poll_free);
+ m_simplePollSetFunc = reinterpret_cast<AvahiSimplePollSetFuncPtr>(&avahi_simple_poll_set_func);
m_clientNew = reinterpret_cast<AvahiClientNewPtr>(&avahi_client_new);
m_clientFree = reinterpret_cast<AvahiClientFreePtr>(&avahi_client_free);
m_serviceBrowserNew = reinterpret_cast<AvahiServiceBrowserNewPtr>(&avahi_service_browser_new);
@@ -162,6 +168,7 @@ public:
if (!m_simplePollIterate) qDebug() << name() << " has null m_simplePollIterate";
if (!m_simplePollQuit) qDebug() << name() << " has null m_simplePollQuit";
if (!m_simplePollFree) qDebug() << name() << " has null m_simplePollFree";
+ if (!m_simplePollSetFunc) qDebug() << name() << " has null m_simplePollSetFunc";
if (!m_clientNew) qDebug() << name() << " has null m_clientNew";
if (!m_clientFree) qDebug() << name() << " has null m_clientFree";
if (!m_serviceBrowserNew) qDebug() << name() << " has null m_serviceBrowserNew";
@@ -286,6 +293,7 @@ public:
return kDNSServiceErr_Unknown;
//avahi_strerror(avahi_client_errno(client));
}
+ browser->activateAutoRefresh();
return kDNSServiceErr_NoError;
}
@@ -297,7 +305,7 @@ public:
return 0;
}
- RunLoopStatus processOneEvent(ConnectionRef cRef, qint64 maxLockMs) {
+ RunLoopStatus processOneEvent(MainConnection *, ConnectionRef cRef, qint64 maxLockMs) {
if (!m_simplePollIterate)
return ProcessedFailure;
MyAvahiConnection *connection = reinterpret_cast<MyAvahiConnection *>(cRef);
@@ -309,11 +317,11 @@ public:
}
RunLoopStatus processOneEventBlock(ConnectionRef cRef) {
- return processOneEvent(cRef,-1);
+ return processOneEvent(NULL,cRef,-1);
}
- DNSServiceErrorType createConnection(ConnectionRef *sdRef) {
- if (!m_simplePollNew || !m_clientNew)
+ DNSServiceErrorType createConnection(MainConnection *mainConnection, ConnectionRef *sdRef) {
+ if (!m_simplePollNew || !m_clientNew || !m_simplePollSetFunc)
return kDNSServiceErr_Unknown;
MyAvahiConnection *connection = new MyAvahiConnection;
connection->lib = this;
@@ -327,6 +335,8 @@ public:
delete connection;
return kDNSServiceErr_Unknown;
}
+ typedef void (*AvahiSimplePollSetFuncPtr)(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata);
+ m_simplePollSetFunc(connection->simple_poll, &cAvahiPollFunction, mainConnection);
/* Allocate a new client */
int error;
connection->client = m_clientNew(m_simplePollGet(connection->simple_poll),
@@ -525,6 +535,20 @@ extern "C" void cAvahiBrowseReply(
}
}
+extern "C" int cAvahiPollFunction(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata)
+{
+ MainConnection *mainConnection = static_cast<MainConnection *>(userdata);
+ QMutex *lock = 0;
+ if (mainConnection)
+ lock = mainConnection->mainThreadLock();
+ if (lock)
+ lock->unlock();
+ int res=poll(ufds,nfds,timeout);
+ if (lock)
+ lock->lock();
+ return res;
+}
+
} // namespace Internal
} // namespace ZeroConf
diff --git a/src/libs/zeroconf/dnsSdLib.cpp b/src/libs/zeroconf/dnsSdLib.cpp
index 603e291740..eb579d6010 100644
--- a/src/libs/zeroconf/dnsSdLib.cpp
+++ b/src/libs/zeroconf/dnsSdLib.cpp
@@ -138,6 +138,10 @@ public:
m_createConnection = reinterpret_cast<CreateConnectionPtr>(&DNSServiceCreateConnection);
m_refSockFD = reinterpret_cast<RefSockFDPtr>(&DNSServiceRefSockFD);
#endif
+ if (m_isOk && m_getAddrInfo == 0) {
+ m_isOk = false;
+ m_errorMsg = tr("*WARNING* DnsSdZConfLib detected an obsolete version of Apple Bonjour, either disable/uninstall it or upgrade it. Otherwise zeroconf will fail.");
+ }
if (DEBUG_ZEROCONF){
if (m_refDeallocate == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_refDeallocate == 0");
if (m_resolve == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_resolve == 0");
@@ -284,7 +288,7 @@ public:
return ProcessedOk;
}
- DNSServiceErrorType createConnection(ConnectionRef *sdRef)
+ DNSServiceErrorType createConnection(MainConnection *, ConnectionRef *sdRef)
{
if (m_createConnection == 0) return kDNSServiceErr_Unsupported;
return m_createConnection(reinterpret_cast<DNSServiceRef *>(sdRef));
@@ -299,7 +303,6 @@ public:
ZConfLib::Ptr ZConfLib::createDnsSdLib(const QString &libName, ZConfLib::Ptr fallback) {
return ZConfLib::Ptr(new DnsSdZConfLib(libName, fallback));
- return fallback;
}
} // namespace Internal
} // namespace ZeroConf
diff --git a/src/libs/zeroconf/embed/CommonServices.h b/src/libs/zeroconf/embed/CommonServices.h
new file mode 100644
index 0000000000..a1c1f7a11d
--- /dev/null
+++ b/src/libs/zeroconf/embed/CommonServices.h
@@ -0,0 +1,1518 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header CommonServices
+
+ Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE.
+*/
+
+#ifndef __COMMON_SERVICES__
+#define __COMMON_SERVICES__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if 0
+#pragma mark == Target ==
+#endif
+
+//===========================================================================================================================
+// Target
+//===========================================================================================================================
+
+// Macintosh
+
+#if( !defined( TARGET_OS_MAC ) )
+ #if( ( macintosh || __MACH__ ) && !KERNEL )
+ // ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #define TARGET_OS_MAC 0
+ #endif
+#endif
+
+#if( !defined( TARGET_API_MAC_OSX_KERNEL ) )
+ #if( __MACH__ && KERNEL )
+ #define TARGET_API_MAC_OSX_KERNEL 1
+ #else
+ #define TARGET_API_MAC_OSX_KERNEL 0
+ #endif
+#endif
+
+// Linux
+
+#if( !defined( TARGET_OS_LINUX ) )
+ #if( defined( __linux__ ) )
+ #define TARGET_OS_LINUX 1
+ #else
+ #define TARGET_OS_LINUX 0
+ #endif
+#endif
+
+// Solaris
+
+#if( !defined( TARGET_OS_SOLARIS ) )
+ #if( defined(solaris) || (defined(__SVR4) && defined(sun)) )
+ #define TARGET_OS_SOLARIS 1
+ #else
+ #define TARGET_OS_SOLARIS 0
+ #endif
+#endif
+
+// Palm
+
+#if( !defined( TARGET_OS_PALM ) )
+ #if( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) )
+ #define TARGET_OS_PALM 1
+ #else
+ #define TARGET_OS_PALM 0
+ #endif
+#endif
+
+// VxWorks
+
+#if( !defined( TARGET_OS_VXWORKS ) )
+
+ // No predefined macro for VxWorks so just assume VxWorks if nothing else is set.
+
+ #if( !macintosh && !__MACH__ && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) )
+ #define TARGET_OS_VXWORKS 1
+ #else
+ #define TARGET_OS_VXWORKS 0
+ #endif
+#endif
+
+// Windows
+
+#if( !defined( TARGET_OS_WIN32 ) )
+ #if( macintosh || __MACH__ )
+ // ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #if( defined( _WIN32 ) )
+ #define TARGET_OS_WIN32 1
+ #else
+ #define TARGET_OS_WIN32 0
+ #endif
+ #endif
+#endif
+
+// Windows CE
+
+#if( !defined( TARGET_OS_WINDOWS_CE ) )
+ #if( defined( _WIN32_WCE ) )
+ #define TARGET_OS_WINDOWS_CE 1
+ #else
+ #define TARGET_OS_WINDOWS_CE 0
+ #endif
+#endif
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+ #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF)
+ #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+ #endif
+ #include <stddef.h>
+#endif
+
+#if( ( macintosh || __MACH__ ) && !KERNEL )
+
+ #if( defined( __MWERKS__ ) )
+ #if( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+ #else
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #if( __MACH__ )
+
+ // Mac OS X
+
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <fcntl.h>
+ #include <pthread.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <unistd.h>
+
+ #else
+
+ // Classic Mac OS
+
+ #include <ConditionalMacros.h>
+ #include <MacTypes.h>
+
+ #endif
+
+#elif( KERNEL )
+
+ // Mac OS X Kernel
+
+ #include <stdint.h>
+
+ #include <libkern/OSTypes.h>
+ #include <sys/types.h>
+
+#elif( TARGET_OS_LINUX )
+
+ // Linux
+
+ #include <stdint.h>
+ #include <arpa/inet.h>
+
+#elif( TARGET_OS_SOLARIS )
+
+ // Solaris
+
+ #include <stdint.h>
+
+ #include <arpa/inet.h>
+ #include <arpa/nameser.h>
+
+ #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+ #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+
+#elif( TARGET_OS_PALM )
+
+ // Palm (no special includes yet).
+
+#elif( TARGET_OS_VXWORKS )
+
+ // VxWorks
+
+ #include "vxWorks.h"
+
+#elif( TARGET_OS_WIN32 )
+
+ // Windows
+
+ #if( !defined( WIN32_WINDOWS ) )
+ #define WIN32_WINDOWS 0x0401
+ #endif
+
+ #if( !defined( _WIN32_WINDOWS ) )
+ #define _WIN32_WINDOWS 0x0401
+ #endif
+
+ #if( !defined( WIN32_LEAN_AND_MEAN ) )
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+ #endif
+
+ #if( defined( __MWERKS__ ) )
+
+ #if( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #elif( defined( _MSC_VER ) )
+
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+ #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers.
+
+ #endif
+
+ #include <windows.h>
+ #include <winsock2.h>
+ #include <Ws2tcpip.h>
+
+ #if( defined( _MSC_VER ) )
+ #pragma warning( default:4706 )
+ #endif
+
+#else
+ #error unknown OS - update this file to support your OS
+#endif
+
+#if( !defined( TARGET_BUILD_MAIN ) )
+ #if( !TARGET_OS_VXWORKS )
+ #define TARGET_BUILD_MAIN 1
+ #endif
+#endif
+
+#if( __GNUC__ || !TARGET_OS_VXWORKS )
+ #define TARGET_LANGUAGE_C_LIKE 1
+#else
+ #define TARGET_LANGUAGE_C_LIKE 0
+#endif
+
+#if 0
+#pragma mark == CPU ==
+#endif
+
+//===========================================================================================================================
+// CPU
+//===========================================================================================================================
+
+// PowerPC
+
+#if( !defined( TARGET_CPU_PPC ) )
+ #if( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) )
+ #define TARGET_CPU_PPC 1
+ #else
+ #define TARGET_CPU_PPC 0
+ #endif
+#endif
+
+// x86
+
+#if( !defined( TARGET_CPU_X86 ) )
+ #if( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) )
+ #define TARGET_CPU_X86 1
+ #else
+ #define TARGET_CPU_X86 0
+ #endif
+#endif
+
+// MIPS
+
+#if( !defined( TARGET_CPU_MIPS ) )
+ #if( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) )
+ #define TARGET_CPU_MIPS 1
+ #else
+ #define TARGET_CPU_MIPS 0
+ #endif
+#endif
+
+#if( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) )
+ #error unknown CPU - update this file to support your CPU
+#endif
+
+#if 0
+#pragma mark == Byte Order ==
+#endif
+
+//===========================================================================================================================
+// Byte Order
+//===========================================================================================================================
+
+// TARGET_RT_LITTLE_ENDIAN
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \
+ TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #endif
+#endif
+
+// TARGET_RT_BIG_ENDIAN
+
+#if( !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \
+ ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #else
+ #define TARGET_RT_BIG_ENDIAN 0
+ #endif
+#endif
+
+#if( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BIG_ENDIAN 0
+ #else
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+#endif
+
+#if( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if( TARGET_RT_BIG_ENDIAN )
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+#endif
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) )
+ #error unknown byte order - update this file to support your byte order
+#endif
+
+// TARGET_RT_BYTE_ORDER
+
+#if( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER ) )
+ #if( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN
+ #else
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN
+ #endif
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#if( !TARGET_OS_MAC )
+ #define CR '\r'
+#endif
+
+#define LF '\n'
+#define CRSTR "\r"
+#define LFSTR "\n"
+#define CRLF "\r\n"
+#define CRCR "\r\r"
+
+#if 0
+#pragma mark == Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Compatibility
+//===========================================================================================================================
+
+// Macros to allow the same code to work on Windows and other sockets API-compatible platforms.
+
+#if( TARGET_OS_WIN32 )
+ #define close_compat( X ) closesocket( X )
+ #define errno_compat() (int) GetLastError()
+ #define set_errno_compat( X ) SetLastError( X )
+ #define EWOULDBLOCK_compat WSAEWOULDBLOCK
+ #define ETIMEDOUT_compat WSAETIMEDOUT
+ #define ENOTCONN_compat WSAENOTCONN
+ #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET )
+ #define kInvalidSocketRef INVALID_SOCKET
+ #if( TARGET_LANGUAGE_C_LIKE )
+ typedef SOCKET SocketRef;
+ #endif
+#else
+ #define close_compat( X ) close( X )
+ #define errno_compat() errno
+ #define set_errno_compat( X ) do { errno = ( X ); } while( 0 )
+ #define EWOULDBLOCK_compat EWOULDBLOCK
+ #define ETIMEDOUT_compat ETIMEDOUT
+ #define ENOTCONN_compat ENOTCONN
+ #define IsValidSocket( X ) ( ( X ) >= 0 )
+ #define kInvalidSocketRef -1
+ #if( TARGET_LANGUAGE_C_LIKE )
+ typedef int SocketRef;
+ #endif
+#endif
+
+// socklen_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that.
+// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that.
+// - VxWorks
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS )
+ typedef int socklen_t;
+ #endif
+#endif
+
+// ssize_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Mac OS X when not building with BSD headers
+// - Windows
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC)
+ typedef int ssize_t;
+ #endif
+#endif
+
+// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure.
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !defined( AF_INET6 ) )
+ #define sockaddr_storage sockaddr_in
+ #define ss_family sin_family
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LOOPBACK
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \
+ ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \
+ : 0
+#else
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : 0
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LINK_LOCAL
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) )
+#else
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : 0 )
+#endif
+
+// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking
+// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to
+// CreateThread on Windows CE.
+
+#if( TARGET_OS_WINDOWS_CE )
+ #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \
+ (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \
+ (LPDWORD) THREAD_ID_PTR )
+
+ #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT )
+#elif( TARGET_OS_WIN32 )
+ #define _beginthreadex_compat _beginthreadex
+ #define _endthreadex_compat _endthreadex
+#endif
+
+// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed.
+
+#if( defined( _MSC_VER ) )
+ #define inline_compat __inline
+#else
+ #define inline_compat inline
+#endif
+
+// Calling conventions
+
+#if( !defined( CALLBACK_COMPAT ) )
+ #if( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE )
+ #define CALLBACK_COMPAT CALLBACK
+ #else
+ #define CALLBACK_COMPAT
+ #endif
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kSizeCString
+
+ @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen.
+*/
+
+#define kSizeCString ( (size_t) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_array
+
+ @abstract Determines the number of elements in an array.
+*/
+
+#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_element
+
+ @abstract Determines the size of an array element.
+*/
+
+#define sizeof_element( X ) sizeof( X[ 0 ] )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_string
+
+ @abstract Determines the size of a constant C string, excluding the null terminator.
+*/
+
+#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_field
+
+ @abstract Determines the size of a field of a type.
+*/
+
+#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function RoundUp
+
+ @abstract Rounds X up to a multiple of Y.
+*/
+
+#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) - ( ( X ) % ( Y ) ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsAligned
+
+ @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define IsAligned( X, Y ) ( ( ( X ) & ( ( Y ) - 1 ) ) == 0 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsFieldAligned
+
+ @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignDown
+
+ @abstract Aligns X down to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define AlignDown( X, Y ) ( ( X ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignUp
+
+ @abstract Aligns X up to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) - 1 ) ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Min
+
+ @abstract Returns the lesser of X and Y.
+*/
+
+#if( !defined( Min ) )
+ #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Max
+
+ @abstract Returns the greater of X and Y.
+*/
+
+#if( !defined( Max ) )
+ #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function InsertBits
+
+ @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK.
+
+ For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value:
+
+ InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000
+*/
+
+#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) & ~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function ExtractBits
+
+ @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift right to right justify MASK.
+
+ For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example):
+
+ ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3
+*/
+
+#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Stringify
+
+ @abstract Stringify's an expression.
+
+ @discussion
+
+ Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary
+ because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the
+ -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise,
+ the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines).
+
+ For example:
+
+ #define kMyConstant 1
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1"
+
+ Non-preprocessor symbols do not have this issue. For example:
+
+ enum
+ {
+ kMyConstant = 1
+ };
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant"
+
+ See <http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html> for more info on C preprocessor pre-scanning.
+*/
+
+#define Stringify( X ) # X
+#define StringifyExpansion( X ) Stringify( X )
+
+#if 0
+#pragma mark == Types ==
+#endif
+
+#if( TARGET_LANGUAGE_C_LIKE )
+//===========================================================================================================================
+// Standard Types
+//===========================================================================================================================
+
+#if( !defined( INT8_MIN ) )
+
+ #define INT8_MIN SCHAR_MIN
+
+ #if( defined( _MSC_VER ) )
+
+ // C99 stdint.h not supported in VC++/VS.NET yet.
+
+ typedef INT8 int8_t;
+ typedef UINT8 uint8_t;
+ typedef INT16 int16_t;
+ typedef UINT16 uint16_t;
+ typedef INT32 int32_t;
+ typedef UINT32 uint32_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+
+ #elif( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) )
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+ #endif
+
+ typedef int8_t int_least8_t;
+ typedef int16_t int_least16_t;
+ typedef int32_t int_least32_t;
+ typedef int64_t int_least64_t;
+
+ typedef uint8_t uint_least8_t;
+ typedef uint16_t uint_least16_t;
+ typedef uint32_t uint_least32_t;
+ typedef uint64_t uint_least64_t;
+
+ typedef int8_t int_fast8_t;
+ typedef int16_t int_fast16_t;
+ typedef int32_t int_fast32_t;
+ typedef int64_t int_fast64_t;
+
+ typedef uint8_t uint_fast8_t;
+ typedef uint16_t uint_fast16_t;
+ typedef uint32_t uint_fast32_t;
+ typedef uint64_t uint_fast64_t;
+
+ #if (( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE ) && !defined( _WIN32 ) )
+ typedef long int intptr_t;
+ typedef unsigned long int uintptr_t;
+ #endif
+
+#endif
+
+// Macros for minimum-width integer constants
+
+#if( !defined( INT8_C ) )
+ #define INT8_C( value ) value
+#endif
+
+#if( !defined( INT16_C ) )
+ #define INT16_C( value ) value
+#endif
+
+#if( !defined( INT32_C ) )
+ #define INT32_C( value ) value ## L
+#endif
+
+#if( !defined( INT64_C ) )
+ #if( defined( _MSC_VER ) )
+ #define INT64_C( value ) value ## i64
+ #else
+ #define INT64_C( value ) value ## LL
+ #endif
+#endif
+
+#if( !defined( UINT8_C ) )
+ #define UINT8_C( value ) value ## U
+#endif
+
+#if( !defined( UINT16_C ) )
+ #define UINT16_C( value ) value ## U
+#endif
+
+#if( !defined( UINT32_C ) )
+ #define UINT32_C( value ) value ## UL
+#endif
+
+#if( !defined( UINT64_C ) )
+ #if( defined( _MSC_VER ) )
+ #define UINT64_C( value ) value ## UI64
+ #else
+ #define UINT64_C( value ) value ## ULL
+ #endif
+#endif
+
+#if 0
+#pragma mark == bool ==
+#endif
+
+//===========================================================================================================================
+// Boolean Constants and Types
+//===========================================================================================================================
+
+// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though.
+// C99 defines __bool_true_false_are_defined when bool, true, and false are defined.
+// MacTypes.h defines true and false (Mac builds only).
+//
+// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely
+// short-circuit and gets confused by the option( bool ) portion of the conditional.
+
+#if( defined( __MWERKS__ ) )
+
+ // Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line.
+
+ #if( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) )
+ #define COMMON_SERVICES_NEEDS_BOOL 1
+ #else
+ #define COMMON_SERVICES_NEEDS_BOOL 0
+ #endif
+
+ // Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool.
+
+ #if( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) )
+ #define _Bool int
+ #endif
+
+ // Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header,
+ // which defines true and false to map to C++ true and false (which are not enabled). Serenity Now!
+
+ #if( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) )
+ #define true 1
+ #define false 0
+ #endif
+#else
+ #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined )
+#endif
+
+#if( COMMON_SERVICES_NEEDS_BOOL )
+
+ typedef int bool;
+
+ #define bool bool
+
+ #if( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) )
+ #define true 1
+ #define false 0
+ #endif
+
+ #define __bool_true_false_are_defined 1
+#endif
+
+// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h.
+
+#if( TARGET_API_MAC_OSX_KERNEL )
+ #define TYPE_BOOL 1
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef CStr255
+
+ @abstract 255 character null-terminated (C-style) string.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ typedef char CStr255[ 256 ];
+#endif
+
+#endif // TARGET_LANGUAGE_C_LIKE
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined TYPE_LONGLONG_NATIVE
+
+ @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries.
+*/
+
+#if( !defined( TYPE_LONGLONG_NATIVE ) )
+ #if( !TARGET_OS_VXWORKS )
+ #define TYPE_LONGLONG_NATIVE 1
+ #else
+ #define TYPE_LONGLONG_NATIVE 0
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined long_long_compat
+
+ @abstract Compatibility type to map to the closest thing to long long and unsigned long long.
+
+ @discussion
+
+ Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary
+ "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( TARGET_OS_WIN32 )
+ typedef __int64 long_long_compat;
+ typedef unsigned __int64 unsigned_long_long_compat;
+ #else
+ typedef signed long long long_long_compat;
+ typedef unsigned long long unsigned_long_long_compat;
+ #endif
+#endif
+
+#if 0
+#pragma mark == Errors ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum OSStatus
+
+ @abstract Status Code
+
+ @constant kNoErr 0 No error occurred.
+ @constant kInProgressErr 1 Operation in progress.
+ @constant kUnknownErr -6700 Unknown error occurred.
+ @constant kOptionErr -6701 Option was not acceptable.
+ @constant kSelectorErr -6702 Selector passed in is invalid or unknown.
+ @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time).
+ @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable.
+ @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate.
+ @constant kParamCountErr -6706 Incorrect or unsupported number of parameters.
+ @constant kCommandErr -6707 Command invalid or not supported.
+ @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier.
+ @constant kStateErr -6709 Not in appropriate state to perform operation.
+ @constant kRangeErr -6710 Index is out of range or not valid.
+ @constant kRequestErr -6711 Request was improperly formed or not appropriate.
+ @constant kResponseErr -6712 Response was incorrect or out of sequence.
+ @constant kChecksumErr -6713 Checksum does not match the actual data.
+ @constant kNotHandledErr -6714 Operation was not handled (or not handled completely).
+ @constant kVersionErr -6715 Version is not incorrect or not compatibile.
+ @constant kSignatureErr -6716 Signature did not match what was expected.
+ @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format.
+ @constant kNotInitializedErr -6718 Action request before needed services were initialized.
+ @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized.
+ @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use).
+ @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks).
+ @constant kTimeoutErr -6722 Timeout occurred.
+ @constant kCanceledErr -6723 Operation canceled (successful cancel).
+ @constant kAlreadyCanceledErr -6724 Operation has already been canceled.
+ @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid).
+ @constant kDeletedErr -6726 Object has already been deleted.
+ @constant kNotFoundErr -6727 Something was not found.
+ @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation.
+ @constant kNoResourcesErr -6729 Resources unavailable to perform the operation.
+ @constant kDuplicateErr -6730 Duplicate found or something is a duplicate.
+ @constant kImmutableErr -6731 Entity is not changeable.
+ @constant kUnsupportedDataErr -6732 Data is unknown or not supported.
+ @constant kIntegrityErr -6733 Data is corrupt.
+ @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format.
+ @constant kUnsupportedErr -6735 Feature or option is not supported.
+ @constant kUnexpectedErr -6736 Error occurred that was not expected.
+ @constant kValueErr -6737 Value is not appropriate.
+ @constant kNotReadableErr -6738 Could not read or reading is not allowed.
+ @constant kNotWritableErr -6739 Could not write or writing is not allowed.
+ @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified.
+ @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified.
+ @constant kMalformedErr -6742 Something was not formed correctly.
+ @constant kSizeErr -6743 Size was too big, too small, or not appropriate.
+ @constant kNameErr -6744 Name was not correct, allowed, or appropriate.
+ @constant kNotReadyErr -6745 Device or service is not ready.
+ @constant kReadErr -6746 Could not read.
+ @constant kWriteErr -6747 Could not write.
+ @constant kMismatchErr -6748 Something does not match.
+ @constant kDateErr -6749 Date is invalid or out-of-range.
+ @constant kUnderrunErr -6750 Less data than expected.
+ @constant kOverrunErr -6751 More data than expected.
+ @constant kEndingErr -6752 Connection, session, or something is ending.
+ @constant kConnectionErr -6753 Connection failed or could not be established.
+ @constant kAuthenticationErr -6754 Authentication failed or is not supported.
+ @constant kOpenErr -6755 Could not open file, pipe, device, etc.
+ @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.).
+ @constant kSkipErr -6757 Items should be or was skipped.
+ @constant kNoAckErr -6758 No acknowledge.
+ @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time).
+ @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed.
+ @constant kNoAddressAckErr -6761 No acknowledge of address.
+ @constant kBusyErr -6762 Cannot perform because something is busy.
+ @constant kNoSpaceErr -6763 Not enough space to perform operation.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL )
+ typedef int32_t OSStatus;
+ #endif
+#endif
+
+#define kNoErr 0
+#define kInProgressErr 1
+
+// Generic error codes are in the range -6700 to -6779.
+
+#define kGenericErrorBase -6700 // Starting error code for all generic errors.
+
+#define kUnknownErr -6700
+#define kOptionErr -6701
+#define kSelectorErr -6702
+#define kExecutionStateErr -6703
+#define kPathErr -6704
+#define kParamErr -6705
+#define kParamCountErr -6706
+#define kCommandErr -6707
+#define kIDErr -6708
+#define kStateErr -6709
+#define kRangeErr -6710
+#define kRequestErr -6711
+#define kResponseErr -6712
+#define kChecksumErr -6713
+#define kNotHandledErr -6714
+#define kVersionErr -6715
+#define kSignatureErr -6716
+#define kFormatErr -6717
+#define kNotInitializedErr -6718
+#define kAlreadyInitializedErr -6719
+#define kNotInUseErr -6720
+#define kInUseErr -6721
+#define kTimeoutErr -6722
+#define kCanceledErr -6723
+#define kAlreadyCanceledErr -6724
+#define kCannotCancelErr -6725
+#define kDeletedErr -6726
+#define kNotFoundErr -6727
+#define kNoMemoryErr -6728
+#define kNoResourcesErr -6729
+#define kDuplicateErr -6730
+#define kImmutableErr -6731
+#define kUnsupportedDataErr -6732
+#define kIntegrityErr -6733
+#define kIncompatibleErr -6734
+#define kUnsupportedErr -6735
+#define kUnexpectedErr -6736
+#define kValueErr -6737
+#define kNotReadableErr -6738
+#define kNotWritableErr -6739
+#define kBadReferenceErr -6740
+#define kFlagErr -6741
+#define kMalformedErr -6742
+#define kSizeErr -6743
+#define kNameErr -6744
+#define kNotReadyErr -6745
+#define kReadErr -6746
+#define kWriteErr -6747
+#define kMismatchErr -6748
+#define kDateErr -6749
+#define kUnderrunErr -6750
+#define kOverrunErr -6751
+#define kEndingErr -6752
+#define kConnectionErr -6753
+#define kAuthenticationErr -6754
+#define kOpenErr -6755
+#define kTypeErr -6756
+#define kSkipErr -6757
+#define kNoAckErr -6758
+#define kCollisionErr -6759
+#define kBackoffErr -6760
+#define kNoAddressAckErr -6761
+#define kBusyErr -6762
+#define kNoSpaceErr -6763
+
+#define kGenericErrorEnd -6779 // Last generic error code (inclusive)
+
+#if 0
+#pragma mark == Mac Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Mac Compatibility
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum Duration
+
+ @abstract Type used to specify a duration of time.
+
+ @constant kDurationImmediate Indicates no delay/wait time.
+ @constant kDurationMicrosecond Microsecond units.
+ @constant kDurationMillisecond Millisecond units.
+ @constant kDurationSecond Second units.
+ @constant kDurationMinute Minute units.
+ @constant kDurationHour Hour units.
+ @constant kDurationDay Day units.
+ @constant kDurationForever Infinite period of time (no timeout).
+
+ @discussion
+
+ Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example,
+ to wait for 5 seconds you would use "5 * kDurationSecond".
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !TARGET_OS_MAC )
+ typedef int32_t Duration;
+ #endif
+#endif
+
+#define kDurationImmediate 0L
+#define kDurationMicrosecond -1L
+#define kDurationMillisecond 1L
+#define kDurationSecond ( 1000L * kDurationMillisecond )
+#define kDurationMinute ( 60L * kDurationSecond )
+#define kDurationHour ( 60L * kDurationMinute )
+#define kDurationDay ( 24L * kDurationHour )
+#define kDurationForever 0x7FFFFFFFL
+
+// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions
+
+#define kNanosecondsPerMicrosecond 1000
+#define kNanosecondsPerMillisecond 1000000
+#define kNanosecondsPerSecond 1000000000
+#define kMicrosecondsPerSecond 1000000
+#define kMicrosecondsPerMillisecond 1000
+#define kMillisecondsPerSecond 1000
+#define kSecondsPerMinute 60
+#define kSecondsPerHour ( 60 * 60 ) // 3600
+#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400
+#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800
+#define kMinutesPerHour 60
+#define kMinutesPerDay ( 60 * 24 ) // 1440
+#define kHoursPerDay 24
+#define kDaysPerWeek 7
+#define kWeeksPerYear 52
+#define kMonthsPerYear 12
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined VersionStages
+
+ @abstract NumVersion-style version stages.
+*/
+
+#define kVersionStageDevelopment 0x20
+#define kVersionStageAlpha 0x40
+#define kVersionStageBeta 0x60
+#define kVersionStageFinal 0x80
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionBuild
+
+ @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4).
+*/
+
+#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \
+ ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \
+ ( ( ( MINOR ) & 0x0F ) << 20 ) | \
+ ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \
+ ( ( ( STAGE ) & 0xFF ) << 8 ) | \
+ ( ( ( REV ) & 0xFF ) ) )
+
+#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) )
+#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) )
+#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) )
+#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) )
+#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) )
+#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionCompare
+
+ @abstract Compares two NumVersion values and returns the following values:
+
+ left < right -> -1
+ left > right -> 1
+ left = right -> 0
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ int NumVersionCompare( uint32_t inLeft, uint32_t inRight );
+#endif
+
+#if 0
+#pragma mark == Binary Constants ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_4
+
+ @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA).
+*/
+
+#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) )
+#define binary_4_hex_wrap( a ) binary_4_hex( a )
+#define binary_4_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_8
+
+ @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B).
+*/
+
+#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) )
+#define binary_8_hex_wrap( a ) binary_8_hex( a )
+#define binary_8_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_16
+
+ @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B).
+*/
+
+#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) )
+#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b )
+#define binary_16_hex( a, b ) ( 0x ## a ## b )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_32
+
+ @abstract Macro to generate an 32-bit constant using binary notation
+ (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B).
+*/
+
+#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) )
+#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d )
+#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d )
+
+// Binary Constant Helpers
+
+#define hex_digit8( a ) HEX_DIGIT_ ## a
+#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a
+
+#define HEX_DIGIT_00000000 00
+#define HEX_DIGIT_00000001 01
+#define HEX_DIGIT_00000010 02
+#define HEX_DIGIT_00000011 03
+#define HEX_DIGIT_00000100 04
+#define HEX_DIGIT_00000101 05
+#define HEX_DIGIT_00000110 06
+#define HEX_DIGIT_00000111 07
+#define HEX_DIGIT_00001000 08
+#define HEX_DIGIT_00001001 09
+#define HEX_DIGIT_00001010 0A
+#define HEX_DIGIT_00001011 0B
+#define HEX_DIGIT_00001100 0C
+#define HEX_DIGIT_00001101 0D
+#define HEX_DIGIT_00001110 0E
+#define HEX_DIGIT_00001111 0F
+#define HEX_DIGIT_00010000 10
+#define HEX_DIGIT_00010001 11
+#define HEX_DIGIT_00010010 12
+#define HEX_DIGIT_00010011 13
+#define HEX_DIGIT_00010100 14
+#define HEX_DIGIT_00010101 15
+#define HEX_DIGIT_00010110 16
+#define HEX_DIGIT_00010111 17
+#define HEX_DIGIT_00011000 18
+#define HEX_DIGIT_00011001 19
+#define HEX_DIGIT_00011010 1A
+#define HEX_DIGIT_00011011 1B
+#define HEX_DIGIT_00011100 1C
+#define HEX_DIGIT_00011101 1D
+#define HEX_DIGIT_00011110 1E
+#define HEX_DIGIT_00011111 1F
+#define HEX_DIGIT_00100000 20
+#define HEX_DIGIT_00100001 21
+#define HEX_DIGIT_00100010 22
+#define HEX_DIGIT_00100011 23
+#define HEX_DIGIT_00100100 24
+#define HEX_DIGIT_00100101 25
+#define HEX_DIGIT_00100110 26
+#define HEX_DIGIT_00100111 27
+#define HEX_DIGIT_00101000 28
+#define HEX_DIGIT_00101001 29
+#define HEX_DIGIT_00101010 2A
+#define HEX_DIGIT_00101011 2B
+#define HEX_DIGIT_00101100 2C
+#define HEX_DIGIT_00101101 2D
+#define HEX_DIGIT_00101110 2E
+#define HEX_DIGIT_00101111 2F
+#define HEX_DIGIT_00110000 30
+#define HEX_DIGIT_00110001 31
+#define HEX_DIGIT_00110010 32
+#define HEX_DIGIT_00110011 33
+#define HEX_DIGIT_00110100 34
+#define HEX_DIGIT_00110101 35
+#define HEX_DIGIT_00110110 36
+#define HEX_DIGIT_00110111 37
+#define HEX_DIGIT_00111000 38
+#define HEX_DIGIT_00111001 39
+#define HEX_DIGIT_00111010 3A
+#define HEX_DIGIT_00111011 3B
+#define HEX_DIGIT_00111100 3C
+#define HEX_DIGIT_00111101 3D
+#define HEX_DIGIT_00111110 3E
+#define HEX_DIGIT_00111111 3F
+#define HEX_DIGIT_01000000 40
+#define HEX_DIGIT_01000001 41
+#define HEX_DIGIT_01000010 42
+#define HEX_DIGIT_01000011 43
+#define HEX_DIGIT_01000100 44
+#define HEX_DIGIT_01000101 45
+#define HEX_DIGIT_01000110 46
+#define HEX_DIGIT_01000111 47
+#define HEX_DIGIT_01001000 48
+#define HEX_DIGIT_01001001 49
+#define HEX_DIGIT_01001010 4A
+#define HEX_DIGIT_01001011 4B
+#define HEX_DIGIT_01001100 4C
+#define HEX_DIGIT_01001101 4D
+#define HEX_DIGIT_01001110 4E
+#define HEX_DIGIT_01001111 4F
+#define HEX_DIGIT_01010000 50
+#define HEX_DIGIT_01010001 51
+#define HEX_DIGIT_01010010 52
+#define HEX_DIGIT_01010011 53
+#define HEX_DIGIT_01010100 54
+#define HEX_DIGIT_01010101 55
+#define HEX_DIGIT_01010110 56
+#define HEX_DIGIT_01010111 57
+#define HEX_DIGIT_01011000 58
+#define HEX_DIGIT_01011001 59
+#define HEX_DIGIT_01011010 5A
+#define HEX_DIGIT_01011011 5B
+#define HEX_DIGIT_01011100 5C
+#define HEX_DIGIT_01011101 5D
+#define HEX_DIGIT_01011110 5E
+#define HEX_DIGIT_01011111 5F
+#define HEX_DIGIT_01100000 60
+#define HEX_DIGIT_01100001 61
+#define HEX_DIGIT_01100010 62
+#define HEX_DIGIT_01100011 63
+#define HEX_DIGIT_01100100 64
+#define HEX_DIGIT_01100101 65
+#define HEX_DIGIT_01100110 66
+#define HEX_DIGIT_01100111 67
+#define HEX_DIGIT_01101000 68
+#define HEX_DIGIT_01101001 69
+#define HEX_DIGIT_01101010 6A
+#define HEX_DIGIT_01101011 6B
+#define HEX_DIGIT_01101100 6C
+#define HEX_DIGIT_01101101 6D
+#define HEX_DIGIT_01101110 6E
+#define HEX_DIGIT_01101111 6F
+#define HEX_DIGIT_01110000 70
+#define HEX_DIGIT_01110001 71
+#define HEX_DIGIT_01110010 72
+#define HEX_DIGIT_01110011 73
+#define HEX_DIGIT_01110100 74
+#define HEX_DIGIT_01110101 75
+#define HEX_DIGIT_01110110 76
+#define HEX_DIGIT_01110111 77
+#define HEX_DIGIT_01111000 78
+#define HEX_DIGIT_01111001 79
+#define HEX_DIGIT_01111010 7A
+#define HEX_DIGIT_01111011 7B
+#define HEX_DIGIT_01111100 7C
+#define HEX_DIGIT_01111101 7D
+#define HEX_DIGIT_01111110 7E
+#define HEX_DIGIT_01111111 7F
+#define HEX_DIGIT_10000000 80
+#define HEX_DIGIT_10000001 81
+#define HEX_DIGIT_10000010 82
+#define HEX_DIGIT_10000011 83
+#define HEX_DIGIT_10000100 84
+#define HEX_DIGIT_10000101 85
+#define HEX_DIGIT_10000110 86
+#define HEX_DIGIT_10000111 87
+#define HEX_DIGIT_10001000 88
+#define HEX_DIGIT_10001001 89
+#define HEX_DIGIT_10001010 8A
+#define HEX_DIGIT_10001011 8B
+#define HEX_DIGIT_10001100 8C
+#define HEX_DIGIT_10001101 8D
+#define HEX_DIGIT_10001110 8E
+#define HEX_DIGIT_10001111 8F
+#define HEX_DIGIT_10010000 90
+#define HEX_DIGIT_10010001 91
+#define HEX_DIGIT_10010010 92
+#define HEX_DIGIT_10010011 93
+#define HEX_DIGIT_10010100 94
+#define HEX_DIGIT_10010101 95
+#define HEX_DIGIT_10010110 96
+#define HEX_DIGIT_10010111 97
+#define HEX_DIGIT_10011000 98
+#define HEX_DIGIT_10011001 99
+#define HEX_DIGIT_10011010 9A
+#define HEX_DIGIT_10011011 9B
+#define HEX_DIGIT_10011100 9C
+#define HEX_DIGIT_10011101 9D
+#define HEX_DIGIT_10011110 9E
+#define HEX_DIGIT_10011111 9F
+#define HEX_DIGIT_10100000 A0
+#define HEX_DIGIT_10100001 A1
+#define HEX_DIGIT_10100010 A2
+#define HEX_DIGIT_10100011 A3
+#define HEX_DIGIT_10100100 A4
+#define HEX_DIGIT_10100101 A5
+#define HEX_DIGIT_10100110 A6
+#define HEX_DIGIT_10100111 A7
+#define HEX_DIGIT_10101000 A8
+#define HEX_DIGIT_10101001 A9
+#define HEX_DIGIT_10101010 AA
+#define HEX_DIGIT_10101011 AB
+#define HEX_DIGIT_10101100 AC
+#define HEX_DIGIT_10101101 AD
+#define HEX_DIGIT_10101110 AE
+#define HEX_DIGIT_10101111 AF
+#define HEX_DIGIT_10110000 B0
+#define HEX_DIGIT_10110001 B1
+#define HEX_DIGIT_10110010 B2
+#define HEX_DIGIT_10110011 B3
+#define HEX_DIGIT_10110100 B4
+#define HEX_DIGIT_10110101 B5
+#define HEX_DIGIT_10110110 B6
+#define HEX_DIGIT_10110111 B7
+#define HEX_DIGIT_10111000 B8
+#define HEX_DIGIT_10111001 B9
+#define HEX_DIGIT_10111010 BA
+#define HEX_DIGIT_10111011 BB
+#define HEX_DIGIT_10111100 BC
+#define HEX_DIGIT_10111101 BD
+#define HEX_DIGIT_10111110 BE
+#define HEX_DIGIT_10111111 BF
+#define HEX_DIGIT_11000000 C0
+#define HEX_DIGIT_11000001 C1
+#define HEX_DIGIT_11000010 C2
+#define HEX_DIGIT_11000011 C3
+#define HEX_DIGIT_11000100 C4
+#define HEX_DIGIT_11000101 C5
+#define HEX_DIGIT_11000110 C6
+#define HEX_DIGIT_11000111 C7
+#define HEX_DIGIT_11001000 C8
+#define HEX_DIGIT_11001001 C9
+#define HEX_DIGIT_11001010 CA
+#define HEX_DIGIT_11001011 CB
+#define HEX_DIGIT_11001100 CC
+#define HEX_DIGIT_11001101 CD
+#define HEX_DIGIT_11001110 CE
+#define HEX_DIGIT_11001111 CF
+#define HEX_DIGIT_11010000 D0
+#define HEX_DIGIT_11010001 D1
+#define HEX_DIGIT_11010010 D2
+#define HEX_DIGIT_11010011 D3
+#define HEX_DIGIT_11010100 D4
+#define HEX_DIGIT_11010101 D5
+#define HEX_DIGIT_11010110 D6
+#define HEX_DIGIT_11010111 D7
+#define HEX_DIGIT_11011000 D8
+#define HEX_DIGIT_11011001 D9
+#define HEX_DIGIT_11011010 DA
+#define HEX_DIGIT_11011011 DB
+#define HEX_DIGIT_11011100 DC
+#define HEX_DIGIT_11011101 DD
+#define HEX_DIGIT_11011110 DE
+#define HEX_DIGIT_11011111 DF
+#define HEX_DIGIT_11100000 E0
+#define HEX_DIGIT_11100001 E1
+#define HEX_DIGIT_11100010 E2
+#define HEX_DIGIT_11100011 E3
+#define HEX_DIGIT_11100100 E4
+#define HEX_DIGIT_11100101 E5
+#define HEX_DIGIT_11100110 E6
+#define HEX_DIGIT_11100111 E7
+#define HEX_DIGIT_11101000 E8
+#define HEX_DIGIT_11101001 E9
+#define HEX_DIGIT_11101010 EA
+#define HEX_DIGIT_11101011 EB
+#define HEX_DIGIT_11101100 EC
+#define HEX_DIGIT_11101101 ED
+#define HEX_DIGIT_11101110 EE
+#define HEX_DIGIT_11101111 EF
+#define HEX_DIGIT_11110000 F0
+#define HEX_DIGIT_11110001 F1
+#define HEX_DIGIT_11110010 F2
+#define HEX_DIGIT_11110011 F3
+#define HEX_DIGIT_11110100 F4
+#define HEX_DIGIT_11110101 F5
+#define HEX_DIGIT_11110110 F6
+#define HEX_DIGIT_11110111 F7
+#define HEX_DIGIT_11111000 F8
+#define HEX_DIGIT_11111001 F9
+#define HEX_DIGIT_11111010 FA
+#define HEX_DIGIT_11111011 FB
+#define HEX_DIGIT_11111100 FC
+#define HEX_DIGIT_11111101 FD
+#define HEX_DIGIT_11111110 FE
+#define HEX_DIGIT_11111111 FF
+
+#if 0
+#pragma mark == Debugging ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function CommonServicesTest
+
+ @abstract Unit test.
+*/
+
+#if( DEBUG )
+ #if( TARGET_LANGUAGE_C_LIKE )
+ OSStatus CommonServicesTest( void );
+ #endif
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __COMMON_SERVICES__
diff --git a/src/libs/zeroconf/embed/DebugServices.c b/src/libs/zeroconf/embed/DebugServices.c
new file mode 100644
index 0000000000..647329628c
--- /dev/null
+++ b/src/libs/zeroconf/embed/DebugServices.c
@@ -0,0 +1,3075 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To Do:
+
+ - Use StackWalk on Windows to optionally print stack frames.
+*/
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+ #include <ctype.h>
+ #include <stdio.h>
+ #include <string.h>
+#endif
+
+#include "CommonServices.h"
+
+#include "DebugServices.h"
+
+#if( DEBUG )
+
+#if( TARGET_OS_VXWORKS )
+ #include "intLib.h"
+#endif
+
+#if( TARGET_OS_WIN32 )
+ #include <time.h>
+
+ #if( !TARGET_OS_WINDOWS_CE )
+ #include <fcntl.h>
+ #include <io.h>
+ #endif
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL )
+ #include <IOKit/IOLib.h>
+#endif
+
+// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h.
+
+#if( defined( MDNS_DEBUGMSGS ) )
+ #include "mDNSEmbeddedAPI.h"
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) )
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize );
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+ static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename );
+ static void DebugFPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// iDebug (Mac OS X user and kernel)
+
+#if( DEBUG_IDEBUG_ENABLED )
+ static OSStatus DebugiDebugInit( void );
+ static void DebugiDebugPrint( char *inData, size_t inSize );
+#endif
+
+// kprintf (Mac OS X Kernel)
+
+#if( DEBUG_KPRINTF_ENABLED )
+ static void DebugKPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X IOLog (Mac OS X Kernel)
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ static void DebugMacOSXIOLogPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X Log
+
+#if( TARGET_OS_MAC )
+ static OSStatus DebugMacOSXLogInit( void );
+ static void DebugMacOSXLogPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Debugger
+
+#if( TARGET_OS_WIN32 )
+ static void DebugWindowsDebuggerPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Event Log
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule );
+ static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize );
+#endif
+
+// DebugLib support
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ static pascal void
+ DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertionString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg );
+#endif
+
+// Utilities
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString );
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static void DebugWinEnableConsole( void );
+#endif
+
+#if( TARGET_OS_WIN32 )
+ static TCHAR *
+ DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount );
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Private Globals
+//===========================================================================================================================
+
+#if( TARGET_OS_VXWORKS )
+ // TCP States for inetstatShow.
+
+ extern char ** pTcpstates; // defined in tcpLib.c
+
+ const char * kDebugTCPStates[] =
+ {
+ "(0) TCPS_CLOSED",
+ "(1) TCPS_LISTEN",
+ "(2) TCPS_SYN_SENT",
+ "(3) TCPS_SYN_RECEIVED",
+ "(4) TCPS_ESTABLISHED",
+ "(5) TCPS_CLOSE_WAIT",
+ "(6) TCPS_FIN_WAIT_1",
+ "(7) TCPS_CLOSING",
+ "(8) TCPS_LAST_ACK",
+ "(9) TCPS_FIN_WAIT_2",
+ "(10) TCPS_TIME_WAIT",
+ };
+#endif
+
+// General
+
+static bool gDebugInitialized = false;
+static DebugOutputType gDebugOutputType = kDebugOutputTypeNone;
+static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo;
+static DebugLevel gDebugPrintLevelMax = kDebugLevelMax;
+static DebugLevel gDebugBreakLevel = kDebugLevelAssert;
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL;
+#endif
+
+// Custom
+
+static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL;
+static void * gDebugCustomOutputContext = NULL;
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+ static FILE * gDebugFPrintFFile = NULL;
+#endif
+
+// MacOSXLog
+
+#if( TARGET_OS_MAC )
+ typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... );
+
+ static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL;
+#endif
+
+// WindowsEventLog
+
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static HANDLE gDebugWindowsEventLogEventSource = NULL;
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == General ==
+#endif
+
+//===========================================================================================================================
+// DebugInitialize
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... )
+{
+ OSStatus err;
+ DebugOutputType type;
+ va_list args;
+
+ va_start( args, inType );
+
+#if( TARGET_OS_VXWORKS )
+ // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason).
+
+ if( !pTcpstates )
+ {
+ pTcpstates = (char **) kDebugTCPStates;
+ }
+#endif
+
+ // Set up DebugLib stuff (if building with Debugging.h).
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ if( !gDebugAssertOutputHandlerUPP )
+ {
+ gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler );
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP );
+ }
+ }
+#endif
+
+ // Pre-process meta-output kind to pick an appropriate output kind for the platform.
+
+ type = inType;
+ if( type == kDebugOutputTypeMetaConsole )
+ {
+ #if( TARGET_OS_MAC )
+ type = kDebugOutputTypeMacOSXLog;
+ #elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ type = kDebugOutputTypeWindowsDebugger;
+ #endif
+ #elif( TARGET_API_MAC_OSX_KERNEL )
+ #if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ type = kDebugOutputTypeMacOSXIOLog;
+ #elif( DEBUG_IDEBUG_ENABLED )
+ type = kDebugOutputTypeiDebug;
+ #elif( DEBUG_KPRINTF_ENABLED )
+ type = kDebugOutputTypeKPrintF;
+ #endif
+ #elif( TARGET_OS_VXWORKS )
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ #error target is VxWorks, but fprintf output is disabled
+ #endif
+ #else
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #endif
+ #endif
+ }
+
+ // Process output kind.
+
+ gDebugOutputType = type;
+ switch( type )
+ {
+ case kDebugOutputTypeNone:
+ err = kNoErr;
+ break;
+
+ case kDebugOutputTypeCustom:
+ gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr );
+ gDebugCustomOutputContext = va_arg( args, void * );
+ err = kNoErr;
+ break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ if( inType == kDebugOutputTypeMetaConsole )
+ {
+ err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL );
+ }
+ else
+ {
+ DebugOutputTypeFlags flags;
+ const char * filename;
+
+ flags = (DebugOutputTypeFlags) va_arg( args, unsigned int );
+ if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile )
+ {
+ filename = va_arg( args, const char * );
+ }
+ else
+ {
+ filename = NULL;
+ }
+ err = DebugFPrintFInit( flags, filename );
+ }
+ break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ err = DebugiDebugInit();
+ break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ err = kNoErr;
+ break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ err = kNoErr;
+ break;
+#endif
+
+#if( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ err = DebugMacOSXLogInit();
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ err = kNoErr;
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ {
+ const char * name;
+ HMODULE module;
+
+ name = va_arg( args, const char * );
+ module = va_arg( args, HMODULE );
+ err = DebugWindowsEventLogInit( name, module );
+ }
+ break;
+#endif
+
+ default:
+ err = kParamErr;
+ goto exit;
+ }
+ gDebugInitialized = true;
+
+exit:
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFinalize
+//===========================================================================================================================
+
+DEBUG_EXPORT void DebugFinalize( void )
+{
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( NULL );
+ DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP );
+ gDebugAssertOutputHandlerUPP = NULL;
+ }
+#endif
+}
+
+//===========================================================================================================================
+// DebugGetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel * level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMin;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMax;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugBreakLevel;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugSetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMin = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMax = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel );
+ gDebugBreakLevel = level;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Output ==
+#endif
+
+//===========================================================================================================================
+// DebugPrintF
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... )
+{
+ va_list args;
+ size_t n;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ va_start( args, inFormat );
+ n = DebugPrintFVAList( inLevel, inFormat, args );
+ va_end( args );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrintFVAList
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs )
+{
+ size_t n;
+ char buffer[ 512 ];
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs );
+ DebugPrint( inLevel, buffer, (size_t) n );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrint
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ OSStatus err;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ err = kRangeErr;
+ goto exit;
+ }
+
+ // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available).
+
+ if( DebugTaskLevel() & kDebugInterruptLevelMask )
+ {
+ #if( TARGET_OS_VXWORKS )
+ logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 );
+ #endif
+
+ err = kExecutionStateErr;
+ goto exit;
+ }
+
+ // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage).
+
+ if( !gDebugInitialized )
+ {
+ debug_initialize( kDebugOutputTypeMetaConsole );
+ }
+
+ // Print based on the current output type.
+
+ switch( gDebugOutputType )
+ {
+ case kDebugOutputTypeNone:
+ break;
+
+ case kDebugOutputTypeCustom:
+ if( gDebugCustomOutputFunction )
+ {
+ gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext );
+ }
+ break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ DebugFPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ DebugiDebugPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ DebugKPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ DebugMacOSXIOLogPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ DebugMacOSXLogPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ DebugWindowsDebuggerPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ DebugWindowsEventLogPrint( inLevel, inData, inSize );
+ break;
+#endif
+
+ default:
+ break;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugPrintAssert
+//
+// Warning: This routine relies on several of the strings being string constants that will exist forever because the
+// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based
+// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant
+// constant strings, but if this function is invoked directly from other places, it must use constant strings.
+//===========================================================================================================================
+
+DEBUG_EXPORT void
+ DebugPrintAssert(
+ int_least32_t inErrorCode,
+ const char * inAssertString,
+ const char * inMessage,
+ const char * inFilename,
+ int_least32_t inLineNumber,
+ const char * inFunction )
+{
+ // Skip if the level is not in the enabled range..
+
+ if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) )
+ {
+ return;
+ }
+
+ if( inErrorCode != 0 )
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] error: %ld (%m)\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inErrorCode, inErrorCode,
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+ else
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] assert: \"%s\" %s\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inAssertString ? inAssertString : "",
+ inMessage ? inMessage : "",
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+
+ // Break into the debugger if enabled.
+
+ #if( TARGET_OS_WIN32 )
+ if( gDebugBreakLevel <= kDebugLevelAssert )
+ {
+ if( IsDebuggerPresent() )
+ {
+ DebugBreak();
+ }
+ }
+ #endif
+}
+
+#if 0
+#pragma mark -
+#endif
+
+#if( DEBUG_FPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugFPrintFInit
+//===========================================================================================================================
+
+static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename )
+{
+ OSStatus err;
+ DebugOutputTypeFlags typeFlags;
+
+ typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask;
+ if( typeFlags == kDebugOutputTypeFlagsStdOut )
+ {
+ #if( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsStdErr )
+ {
+ #if( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsFile )
+ {
+ require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr );
+
+ gDebugFPrintFFile = fopen( inFilename, "a" );
+ require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr );
+ }
+ else
+ {
+ err = kParamErr;
+ goto exit;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFPrintFPrint
+//===========================================================================================================================
+
+static void DebugFPrintFPrint( char *inData, size_t inSize )
+{
+ char * p;
+ char * q;
+
+ // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform.
+
+ p = inData;
+ q = p + inSize;
+ while( p < q )
+ {
+ if( *p == '\r' )
+ {
+ *p = '\n';
+ }
+ ++p;
+ }
+
+ // Write the data and flush.
+
+ if( gDebugFPrintFFile )
+ {
+ fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData );
+ fflush( gDebugFPrintFFile );
+ }
+}
+#endif // DEBUG_FPRINTF_ENABLED
+
+#if( DEBUG_IDEBUG_ENABLED )
+//===========================================================================================================================
+// DebugiDebugInit
+//===========================================================================================================================
+
+static OSStatus DebugiDebugInit( void )
+{
+ OSStatus err;
+
+ #if( TARGET_API_MAC_OSX_KERNEL )
+
+ extern uint32_t * _giDebugReserved1;
+
+ // Emulate the iDebugSetOutputType macro in iDebugServices.h.
+ // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext.
+
+ if( !_giDebugReserved1 )
+ {
+ _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) );
+ require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr );
+ }
+ *_giDebugReserved1 = 0x00010000U;
+ err = kNoErr;
+exit:
+ #else
+
+ __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType );
+
+ iDebugSetOutputTypeInternal( 0x00010000U );
+ err = kNoErr;
+
+ #endif
+
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugiDebugPrint
+//===========================================================================================================================
+
+static void DebugiDebugPrint( char *inData, size_t inSize )
+{
+ #if( TARGET_API_MAC_OSX_KERNEL )
+
+ // Locally declared here so we do not need to include iDebugKext.h.
+ // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the
+ // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present).
+ // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present.
+
+ typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ extern iDebugLogFunctionPtr _giDebugLogInternal;
+
+ if( _giDebugLogInternal )
+ {
+ _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+ }
+
+ #else
+
+ __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+
+ #endif
+}
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugKPrintFPrint
+//===========================================================================================================================
+
+static void DebugKPrintFPrint( char *inData, size_t inSize )
+{
+ extern void kprintf( const char *inFormat, ... );
+
+ kprintf( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+//===========================================================================================================================
+// DebugMacOSXIOLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXIOLogPrint( char *inData, size_t inSize )
+{
+ extern void IOLog( const char *inFormat, ... );
+
+ IOLog( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( TARGET_OS_MAC )
+//===========================================================================================================================
+// DebugMacOSXLogInit
+//===========================================================================================================================
+
+static OSStatus DebugMacOSXLogInit( void )
+{
+ OSStatus err;
+ CFStringRef path;
+ CFURLRef url;
+ CFBundleRef bundle;
+ CFStringRef functionName;
+ void * functionPtr;
+
+ bundle = NULL;
+
+ // Create a bundle reference for System.framework.
+
+ path = CFSTR( "/System/Library/Frameworks/System.framework" );
+ url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true );
+ require_action_quiet( url, exit, err = memFullErr );
+
+ bundle = CFBundleCreate( NULL, url );
+ CFRelease( url );
+ require_action_quiet( bundle, exit, err = memFullErr );
+
+ // Get a ptr to the system's "printf" function from System.framework.
+
+ functionName = CFSTR( "printf" );
+ functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName );
+ require_action_quiet( functionPtr, exit, err = memFullErr );
+
+ // Success! Note: The bundle cannot be released because it would invalidate the function ptr.
+
+ gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr;
+ bundle = NULL;
+ err = noErr;
+
+exit:
+ if( bundle )
+ {
+ CFRelease( bundle );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugMacOSXLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXLogPrint( char *inData, size_t inSize )
+{
+ if( gDebugMacOSXLogFunction )
+ {
+ gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData );
+ }
+}
+#endif
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWindowsDebuggerPrint
+//===========================================================================================================================
+
+void DebugWindowsDebuggerPrint( char *inData, size_t inSize )
+{
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Print out the string to the debugger.
+
+ OutputDebugString( buffer );
+}
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWindowsEventLogInit
+//===========================================================================================================================
+
+static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule )
+{
+ OSStatus err;
+ HKEY key;
+ TCHAR name[ 128 ];
+ const char * src;
+ TCHAR path[ MAX_PATH ];
+ size_t size;
+ DWORD typesSupported;
+ DWORD n;
+
+ key = NULL;
+
+ // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds.
+
+ if( !inName || ( *inName == '\0' ) )
+ {
+ inName = "DefaultApp";
+ }
+ DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL );
+
+ // Build the path string using the fixed registry path and app name.
+
+ src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
+ DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size );
+ DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL );
+
+ // Add/Open the source name as a sub-key under the Application key in the EventLog registry key.
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL );
+ require_noerr_quiet( err, exit );
+
+ // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator.
+
+ n = GetModuleFileName( inModule, path, sizeof_array( path ) );
+ err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+ n += 1;
+ n *= sizeof( TCHAR );
+
+ err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+ require_noerr_quiet( err, exit );
+
+ // Set the supported event types in the TypesSupported subkey.
+
+ typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE |
+ EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
+ err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+ require_noerr_quiet( err, exit );
+
+ // Set up the event source.
+
+ gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name );
+ err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+
+exit:
+ if( key )
+ {
+ RegCloseKey( key );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugWindowsEventLogPrint
+//===========================================================================================================================
+
+static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ WORD type;
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+ const TCHAR * array[ 1 ];
+
+ // Map the debug level to a Windows EventLog type.
+
+ if( inLevel <= kDebugLevelNotice )
+ {
+ type = EVENTLOG_INFORMATION_TYPE;
+ }
+ else if( inLevel <= kDebugLevelWarning )
+ {
+ type = EVENTLOG_WARNING_TYPE;
+ }
+ else
+ {
+ type = EVENTLOG_ERROR_TYPE;
+ }
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Add the the string to the event log.
+
+ array[ 0 ] = buffer;
+ if( gDebugWindowsEventLogEventSource )
+ {
+ ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL );
+ }
+}
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+//===========================================================================================================================
+// DebugAssertOutputHandler
+//===========================================================================================================================
+
+static pascal void
+ DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg )
+{
+ DEBUG_UNUSED( inComponentSignature );
+ DEBUG_UNUSED( inOptions );
+ DEBUG_UNUSED( inExceptionString );
+ DEBUG_UNUSED( inValue );
+ DEBUG_UNUSED( inOutputMsg );
+
+ DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+// DebugSNPrintF
+//
+// Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes:
+//
+// Changed names to avoid name collisions with the mDNS versions.
+// Changed types to standard C types since mDNSEmbeddedAPI.h may not be available.
+// Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.h.
+// Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+// Added %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+// Added %.8a - FIbre Channel address. Arg=ptr to address.
+// Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+// Added %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+// Added %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+// Added %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc.
+// Added %S - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr.
+// Added %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...)
+ {
+ size_t length;
+
+ va_list ptr;
+ va_start(ptr,fmt);
+ length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr);
+ va_end(ptr);
+
+ return(length);
+ }
+
+//===========================================================================================================================
+// DebugSNPrintFVAList - va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg)
+ {
+ static const struct DebugSNPrintF_format
+ {
+ unsigned leftJustify : 1;
+ unsigned forceSign : 1;
+ unsigned zeroPad : 1;
+ unsigned havePrecision : 1;
+ unsigned hSize : 1;
+ char lSize;
+ char altForm;
+ char sign; // +, - or space
+ unsigned int fieldWidth;
+ unsigned int precision;
+ } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ size_t nwritten = 0;
+ int c;
+ if (buflen == 0) return(0);
+ buflen--; // Pre-reserve one space in the buffer for the terminating nul
+ if (buflen == 0) goto exit;
+
+ for (c = *fmt; c != 0; c = *++fmt)
+ {
+ if (c != '%')
+ {
+ *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ }
+ else
+ {
+ size_t i=0, j;
+ // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+ // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+ // The size needs to be enough for a 256-byte domain name plus some error text.
+ #define mDNS_VACB_Size 300
+ char mDNS_VACB[mDNS_VACB_Size];
+ #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+ #define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s))
+ char *s = mDNS_VACB_Lim;
+ const char *digits = "0123456789ABCDEF";
+ struct DebugSNPrintF_format F = DebugSNPrintF_format_default;
+
+ for(;;) // decode flags
+ {
+ c = *++fmt;
+ if (c == '-') F.leftJustify = 1;
+ else if (c == '+') F.forceSign = 1;
+ else if (c == ' ') F.sign = ' ';
+ else if (c == '#') F.altForm++;
+ else if (c == '0') F.zeroPad = 1;
+ else break;
+ }
+
+ if (c == '*') // decode field width
+ {
+ int f = va_arg(arg, int);
+ if (f < 0) { f = -f; F.leftJustify = 1; }
+ F.fieldWidth = (unsigned int)f;
+ c = *++fmt;
+ }
+ else
+ {
+ for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+ }
+
+ if (c == '.') // decode precision
+ {
+ if ((c = *++fmt) == '*')
+ { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+ else for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.precision = (10 * F.precision) + (c - '0');
+ F.havePrecision = 1;
+ }
+
+ if (F.leftJustify) F.zeroPad = 0;
+
+ conv:
+ switch (c) // perform appropriate conversion
+ {
+ #if TYPE_LONGLONG_NATIVE
+ unsigned_long_long_compat n;
+ unsigned_long_long_compat base;
+ #else
+ unsigned long n;
+ unsigned long base;
+ #endif
+ case 'h' : F.hSize = 1; c = *++fmt; goto conv;
+ case 'l' : // fall through
+ case 'L' : F.lSize++; c = *++fmt; goto conv;
+ case 'd' :
+ case 'i' : base = 10;
+ goto canBeSigned;
+ case 'u' : base = 10;
+ goto notSigned;
+ case 'o' : base = 8;
+ goto notSigned;
+ case 'b' : base = 2;
+ goto notSigned;
+ case 'p' : n = va_arg(arg, uintptr_t);
+ F.havePrecision = 1;
+ F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16;
+ F.sign = 0;
+ base = 16;
+ c = 'x';
+ goto number;
+ case 'x' : digits = "0123456789abcdef";
+ case 'X' : base = 16;
+ goto notSigned;
+ canBeSigned:
+ #if TYPE_LONGLONG_NATIVE
+ if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long);
+ else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat);
+ else n = (unsigned_long_long_compat)va_arg(arg, int);
+ #else
+ if (F.lSize == 1) n = (unsigned long)va_arg(arg, long);
+ else if (F.lSize == 2) goto exit;
+ else n = (unsigned long)va_arg(arg, int);
+ #endif
+ if (F.hSize) n = (short) n;
+ #if TYPE_LONGLONG_NATIVE
+ if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; }
+ #else
+ if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+ #endif
+ else if (F.forceSign) F.sign = '+';
+ goto number;
+
+ notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long);
+ else if (F.lSize == 2)
+ {
+ #if TYPE_LONGLONG_NATIVE
+ n = va_arg(arg, unsigned_long_long_compat);
+ #else
+ goto exit;
+ #endif
+ }
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ F.sign = 0;
+ goto number;
+
+ number: if (!F.havePrecision)
+ {
+ if (F.zeroPad)
+ {
+ F.precision = F.fieldWidth;
+ if (F.altForm) F.precision -= 2;
+ if (F.sign) --F.precision;
+ }
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]);
+ for (; i < F.precision; i++) *--s = '0';
+ if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+ if (F.sign) { *--s = F.sign; i++; }
+ break;
+
+ case 'a' : {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ char pre[4] = "";
+ char post[32] = "";
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (F.altForm == 1)
+ {
+ #if(defined(MDNS_DEBUGMSGS))
+ mDNSAddr *ip = (mDNSAddr*)a;
+ switch (ip->type)
+ {
+ case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
+ case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support
+ #endif
+ }
+ else if (F.altForm == 2)
+ {
+ #ifdef AF_INET
+ const struct sockaddr *sa;
+ unsigned char *port;
+ sa = (const struct sockaddr*)a;
+ switch (sa->sa_family)
+ {
+ case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr;
+ port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port;
+ DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break;
+ #ifdef AF_INET6
+ case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr;
+ pre[0] = '['; pre[1] = '\0';
+ port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port;
+ DebugSNPrintF(post, sizeof(post), "%%%d]:%d",
+ (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id,
+ (port[0] << 8) | port[1]); break;
+ #endif
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // socket interfaces not included so no sockaddr support
+ #endif
+ }
+ switch (F.precision)
+ {
+ case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s",
+ a[0], a[1], a[2], a[3], post); break;
+ case 6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5]); break;
+ case 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break;
+ case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB),
+ "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s",
+ pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
+ a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break;
+ default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
+ "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break;
+ }
+ }
+ }
+ break;
+
+ case 'U' : {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]),
+ a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break;
+ }
+ }
+ break;
+
+ case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
+
+ case 'C' : if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ i = 4;
+ break;
+
+ case 's' : s = va_arg(arg, char *);
+ if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else switch (F.altForm)
+ {
+ case 0: i=0;
+ if (F.havePrecision) // C string
+ {
+ while((i < F.precision) && s[i]) i++;
+ // Make sure we don't truncate in the middle of a UTF-8 character.
+ // If the last character is part of a multi-byte UTF-8 character, back up to the start of it.
+ j=0;
+ while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; }
+ // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back.
+ if((j > 1) && (j <= 6))
+ {
+ int test = (0xFF << (8-j)) & 0xFF;
+ int mask = test | (1 << ((8-j)-1));
+ if((c & mask) == test) i += j;
+ }
+ }
+ else
+ while(s[i]) i++;
+ break;
+ case 1: i = (unsigned char) *s++; break; // Pascal string
+ case 2: { // DNS label-sequence name
+ unsigned char *a = (unsigned char *)s;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (*a == 0) *s++ = '.'; // Special case for root DNS name
+ while (*a)
+ {
+ if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+ if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+ s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a);
+ a += 1 + *a;
+ }
+ i = (size_t)(s - mDNS_VACB);
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ break;
+
+ case 'S': { // UTF-16 string
+ unsigned char *a = va_arg(arg, unsigned char *);
+ uint16_t *u = (uint16_t*)a;
+ if (!u) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ if ((!F.havePrecision || F.precision))
+ {
+ if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian
+ else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian
+ }
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ switch (F.altForm)
+ {
+ case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian
+ { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; }
+ break;
+ case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian
+ { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian
+ { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ }
+ }
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+
+ #if TARGET_OS_MAC
+ case '@': { // Cocoa/CoreFoundation object
+ CFTypeRef cfObj;
+ CFStringRef cfStr;
+ cfObj = (CFTypeRef) va_arg(arg, void *);
+ cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (cfStr)
+ {
+ CFRange range;
+ CFIndex m;
+ range = CFRangeMake(0, CFStringGetLength(cfStr));
+ m = 0;
+ CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m);
+ CFRelease(cfStr);
+ i = (size_t) m;
+ }
+ else
+ {
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: <invalid CF object>" );
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ break;
+ #endif
+
+ case 'm' : { // Error Message
+ long err;
+ if (F.lSize) err = va_arg(arg, long);
+ else err = va_arg(arg, int);
+ if (F.hSize) err = (short)err;
+ DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB));
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0;s[i];i++) {}
+ }
+ break;
+
+ case 'H' : { // Hex Dump
+ void *a = va_arg(arg, void *);
+ size_t size = (size_t)va_arg(arg, int);
+ size_t max = (size_t)va_arg(arg, int);
+ DebugFlags flags =
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount;
+ if (F.altForm == 0) flags |= kDebugFlagsNoASCII;
+ size = (max < size) ? max : size;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB));
+ }
+ break;
+
+ case 'v' : { // Version
+ uint32_t version;
+ version = va_arg(arg, unsigned int);
+ DebugNumVersionToString(version, mDNS_VACB);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0;s[i];i++) {}
+ }
+ break;
+
+ case 'n' : s = va_arg(arg, char *);
+ if (F.hSize) * (short *) s = (short)nwritten;
+ else if (F.lSize) * (long *) s = (long)nwritten;
+ else * (int *) s = (int)nwritten;
+ continue;
+
+ default: s = mDNS_VACB;
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+ case '%' : *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ break;
+ }
+
+ if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
+ do {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ } while (i < --F.fieldWidth);
+
+ if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ nwritten += i;
+ if (nwritten >= buflen) goto exit;
+
+ for (; i < F.fieldWidth; i++) // Pad on the right
+ {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ }
+ }
+ }
+ exit:
+ *sbuffer++ = 0;
+ return(nwritten);
+ }
+
+//===========================================================================================================================
+// DebugGetErrorString
+//===========================================================================================================================
+
+DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize )
+{
+ const char * s;
+ char * dst;
+ char * end;
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ char buffer[ 256 ];
+#endif
+
+ switch( inErrorCode )
+ {
+ #define CaseErrorString( X, STR ) case X: s = STR; break
+ #define CaseErrorStringify( X ) case X: s = # X; break
+ #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break
+
+ // General Errors
+
+ CaseErrorString( 0, "no error" );
+ CaseErrorString( 1, "in-progress/waiting" );
+ CaseErrorString( -1, "catch-all unknown error" );
+
+ // ACP Errors
+
+ CaseErrorStringifyHardCode( -2, kACPBadRequestErr );
+ CaseErrorStringifyHardCode( -3, kACPNoMemoryErr );
+ CaseErrorStringifyHardCode( -4, kACPBadParamErr );
+ CaseErrorStringifyHardCode( -5, kACPNotFoundErr );
+ CaseErrorStringifyHardCode( -6, kACPBadChecksumErr );
+ CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr );
+ CaseErrorStringifyHardCode( -8, kACPNetworkErr );
+ CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr );
+ CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr );
+ CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr );
+ CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr );
+ CaseErrorStringifyHardCode( -13, kACPNoResourcesErr );
+ CaseErrorStringifyHardCode( -14, kACPBadOptionErr );
+ CaseErrorStringifyHardCode( -15, kACPBadSizeErr );
+ CaseErrorStringifyHardCode( -16, kACPBadPasswordErr );
+ CaseErrorStringifyHardCode( -17, kACPNotInitializedErr );
+ CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr );
+ CaseErrorStringifyHardCode( -19, kACPBadVersionErr );
+ CaseErrorStringifyHardCode( -20, kACPBadSignatureErr );
+ CaseErrorStringifyHardCode( -21, kACPBadIndexErr );
+ CaseErrorStringifyHardCode( -22, kACPUnsupportedErr );
+ CaseErrorStringifyHardCode( -23, kACPInUseErr );
+ CaseErrorStringifyHardCode( -24, kACPParamCountErr );
+ CaseErrorStringifyHardCode( -25, kACPIDErr );
+ CaseErrorStringifyHardCode( -26, kACPFormatErr );
+ CaseErrorStringifyHardCode( -27, kACPUnknownUserErr );
+ CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr );
+ CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr );
+
+ // Common Services Errors
+
+ CaseErrorStringify( kUnknownErr );
+ CaseErrorStringify( kOptionErr );
+ CaseErrorStringify( kSelectorErr );
+ CaseErrorStringify( kExecutionStateErr );
+ CaseErrorStringify( kPathErr );
+ CaseErrorStringify( kParamErr );
+ CaseErrorStringify( kParamCountErr );
+ CaseErrorStringify( kCommandErr );
+ CaseErrorStringify( kIDErr );
+ CaseErrorStringify( kStateErr );
+ CaseErrorStringify( kRangeErr );
+ CaseErrorStringify( kRequestErr );
+ CaseErrorStringify( kResponseErr );
+ CaseErrorStringify( kChecksumErr );
+ CaseErrorStringify( kNotHandledErr );
+ CaseErrorStringify( kVersionErr );
+ CaseErrorStringify( kSignatureErr );
+ CaseErrorStringify( kFormatErr );
+ CaseErrorStringify( kNotInitializedErr );
+ CaseErrorStringify( kAlreadyInitializedErr );
+ CaseErrorStringify( kNotInUseErr );
+ CaseErrorStringify( kInUseErr );
+ CaseErrorStringify( kTimeoutErr );
+ CaseErrorStringify( kCanceledErr );
+ CaseErrorStringify( kAlreadyCanceledErr );
+ CaseErrorStringify( kCannotCancelErr );
+ CaseErrorStringify( kDeletedErr );
+ CaseErrorStringify( kNotFoundErr );
+ CaseErrorStringify( kNoMemoryErr );
+ CaseErrorStringify( kNoResourcesErr );
+ CaseErrorStringify( kDuplicateErr );
+ CaseErrorStringify( kImmutableErr );
+ CaseErrorStringify( kUnsupportedDataErr );
+ CaseErrorStringify( kIntegrityErr );
+ CaseErrorStringify( kIncompatibleErr );
+ CaseErrorStringify( kUnsupportedErr );
+ CaseErrorStringify( kUnexpectedErr );
+ CaseErrorStringify( kValueErr );
+ CaseErrorStringify( kNotReadableErr );
+ CaseErrorStringify( kNotWritableErr );
+ CaseErrorStringify( kBadReferenceErr );
+ CaseErrorStringify( kFlagErr );
+ CaseErrorStringify( kMalformedErr );
+ CaseErrorStringify( kSizeErr );
+ CaseErrorStringify( kNameErr );
+ CaseErrorStringify( kNotReadyErr );
+ CaseErrorStringify( kReadErr );
+ CaseErrorStringify( kWriteErr );
+ CaseErrorStringify( kMismatchErr );
+ CaseErrorStringify( kDateErr );
+ CaseErrorStringify( kUnderrunErr );
+ CaseErrorStringify( kOverrunErr );
+ CaseErrorStringify( kEndingErr );
+ CaseErrorStringify( kConnectionErr );
+ CaseErrorStringify( kAuthenticationErr );
+ CaseErrorStringify( kOpenErr );
+ CaseErrorStringify( kTypeErr );
+ CaseErrorStringify( kSkipErr );
+ CaseErrorStringify( kNoAckErr );
+ CaseErrorStringify( kCollisionErr );
+ CaseErrorStringify( kBackoffErr );
+ CaseErrorStringify( kNoAddressAckErr );
+ CaseErrorStringify( kBusyErr );
+ CaseErrorStringify( kNoSpaceErr );
+
+ // mDNS/DNS-SD Errors
+
+ CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr );
+ CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr );
+ CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr );
+ CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr );
+ CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr );
+ CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr );
+ CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr );
+ CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr );
+ CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr );
+ CaseErrorStringifyHardCode( -65546, mStatus_NoCache );
+ CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered );
+ CaseErrorStringifyHardCode( -65548, mStatus_NameConflict );
+ CaseErrorStringifyHardCode( -65549, mStatus_Invalid );
+ CaseErrorStringifyHardCode( -65550, mStatus_GrowCache );
+ CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr );
+ CaseErrorStringifyHardCode( -65552, mStatus_Incompatible );
+ CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged );
+ CaseErrorStringifyHardCode( -65792, mStatus_MemFree );
+
+ // RSP Errors
+
+ CaseErrorStringifyHardCode( -400000, kRSPUnknownErr );
+ CaseErrorStringifyHardCode( -400050, kRSPParamErr );
+ CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr );
+ CaseErrorStringifyHardCode( -405246, kRSPRangeErr );
+ CaseErrorStringifyHardCode( -409057, kRSPSizeErr );
+ CaseErrorStringifyHardCode( -400200, kRSPHardwareErr );
+ CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr );
+ CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr );
+ CaseErrorStringifyHardCode( -402419, kRSPIDErr );
+ CaseErrorStringifyHardCode( -403165, kRSPFlagErr );
+ CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" );
+ CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" );
+ CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" );
+ CaseErrorString( -200051, "kRSPChecksumErr - 0x33" );
+ CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" );
+ CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" );
+ CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" );
+
+ // XML Errors
+
+ CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr );
+ CaseErrorStringifyHardCode( -100050, kXMLParamErr );
+ CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr );
+ CaseErrorStringifyHardCode( -100206, kXMLFormatErr );
+ CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr );
+ CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr );
+ CaseErrorStringifyHardCode( -101726, kXMLKeyErr );
+ CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr );
+ CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr );
+ CaseErrorStringifyHardCode( -103026, kXMLParseErr );
+ CaseErrorStringifyHardCode( -103159, kXMLBadDataErr );
+ CaseErrorStringifyHardCode( -103170, kXMLBadNameErr );
+ CaseErrorStringifyHardCode( -105246, kXMLRangeErr );
+ CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr );
+ CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr );
+ CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr );
+ CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr );
+ CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr );
+ CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr );
+ CaseErrorStringifyHardCode( -102015, kXMLDateErr );
+
+ #if( __MACH__ )
+
+ // Mach Errors
+
+ CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE );
+ CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE );
+ CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL );
+ CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL );
+ CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST );
+ CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL );
+ CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY );
+ CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT );
+ CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY );
+ CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER );
+ CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER );
+ CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE );
+ CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME );
+ CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED );
+ CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED );
+ CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET );
+ CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL );
+ CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED );
+
+ // Mach OSReturn Errors
+
+ CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError );
+ CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal );
+ CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances );
+ CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit );
+ CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData );
+ CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts );
+ CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet );
+ CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet );
+ CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper );
+ CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper );
+ CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass );
+
+ // IOKit Errors
+
+ CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError );
+ CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory );
+ CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources );
+ CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError );
+ CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice );
+ CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged );
+ CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument );
+ CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead );
+ CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite );
+ CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess );
+ CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID );
+ CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported );
+ CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError );
+ CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError );
+ CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError );
+ CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock );
+ CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen );
+ CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable );
+ CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable );
+ CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned );
+ CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia );
+ CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen );
+ CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError );
+ CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError );
+ CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy );
+ CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout );
+ CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline );
+ CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady );
+ CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached );
+ CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels );
+ CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace );
+ CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists );
+ CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire );
+ CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt );
+ CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames );
+ CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge );
+ CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted );
+ CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower );
+ CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia );
+ CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia );
+ CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode );
+ CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun );
+ CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun );
+ CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError );
+ CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion );
+ CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted );
+ CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth );
+ CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding );
+ CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld );
+ CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew );
+ CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound );
+ CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid );
+
+ // IOKit FireWire Errors
+
+ CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase );
+ CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset );
+ CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry );
+ CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending );
+ CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken );
+ CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid );
+ CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered );
+ CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers );
+ CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive );
+ CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker );
+ CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels );
+ CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable );
+ CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus );
+ CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs );
+ CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage );
+ CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower );
+ CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels );
+ CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram );
+ CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening );
+ CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept );
+ CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose );
+ CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged );
+ CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged );
+
+ // IOKit USB Errors
+
+ CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr );
+ CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr );
+ CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr );
+ CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound );
+ CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound );
+ CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout );
+ CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned );
+ CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled );
+ CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound );
+ CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError );
+ CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr );
+ CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err );
+ CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err );
+ CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr );
+ CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr );
+ CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err );
+ CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err );
+ CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr );
+ CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr );
+ CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr );
+ CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr );
+ CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr );
+
+ #endif // __MACH__
+
+ // Other Errors
+
+ default:
+ s = NULL;
+ #if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ DWORD n;
+
+ n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode,
+ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL );
+ if( n > 0 )
+ {
+ // Remove any trailing CR's or LF's since some messages have them.
+
+ while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) )
+ {
+ buffer[ --n ] = '\0';
+ }
+ s = buffer;
+ }
+ }
+ #endif
+
+ if( !s )
+ {
+ #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+ s = strerror( inErrorCode );
+ #endif
+ if( !s )
+ {
+ s = "<unknown error code>";
+ }
+ }
+ break;
+ }
+
+ // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string.
+
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ dst = inBuffer;
+ end = dst + ( inBufferSize - 1 );
+ while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) )
+ {
+ *dst++ = *s++;
+ }
+ *dst = '\0';
+ s = inBuffer;
+ }
+ return( s );
+}
+
+//===========================================================================================================================
+// DebugHexDump
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t
+ DebugHexDump(
+ DebugLevel inLevel,
+ int inIndent,
+ const char * inLabel,
+ size_t inLabelSize,
+ int inLabelMinWidth,
+ const char * inType,
+ size_t inTypeSize,
+ const void * inDataStart,
+ const void * inData,
+ size_t inDataSize,
+ DebugFlags inFlags,
+ char * outBuffer,
+ size_t inBufferSize )
+{
+ static const char kHexChars[] = "0123456789ABCDEF";
+ const uint8_t * start;
+ const uint8_t * src;
+ char * dst;
+ char * end;
+ size_t n;
+ int offset;
+ int width;
+ const char * newline;
+ char separator[ 8 ];
+ char * s;
+
+ DEBUG_UNUSED( inType );
+ DEBUG_UNUSED( inTypeSize );
+
+ // Set up the function-wide variables.
+
+ if( inLabelSize == kSizeCString )
+ {
+ inLabelSize = strlen( inLabel );
+ }
+ start = (const uint8_t *) inData;
+ src = start;
+ dst = outBuffer;
+ end = dst + inBufferSize;
+ offset = (int)( (intptr_t) inData - (intptr_t) inDataStart );
+ width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth;
+ newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n";
+
+ // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines.
+
+ s = separator;
+ if( inFlags & kDebugFlagsNoNewLine )
+ {
+ if( inFlags & kDebugFlags8BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( inFlags & kDebugFlags16BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) )
+ {
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - separator ) ) < sizeof( separator ) );
+ }
+ *s = '\0';
+
+ for( ;; )
+ {
+ char prefixString[ 32 ];
+ char hexString[ 64 ];
+ char asciiString[ 32 ];
+ char byteCountString[ 32 ];
+ int c;
+ size_t chunkSize;
+ size_t i;
+
+ // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit.
+
+ if( inDataSize == 0 )
+ {
+ if( inLabel && ( inLabelSize > 0 ) )
+ {
+ width = 0;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ width += 8; // "00000000"
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 1; // "+"
+ }
+ }
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ width += 8; // "00000000"
+ }
+ else if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 4; // "0000"
+ }
+
+ if( outBuffer )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ }
+ break;
+ }
+
+ // Build the prefix string. It will be in one of the following formats:
+ //
+ // 1) "00000000+0000[0000]" (address and offset)
+ // 2) "00000000" (address only)
+ // 3) "0000[0000]" (offset only)
+ // 4) "" (no address or offset)
+ //
+ // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate.
+
+ s = prefixString;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ];
+ *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ];
+
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ *s++ = '+';
+ }
+ }
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ *s++ = kHexChars[ ( offset >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 16 ) & 0xF ];
+ }
+ *s++ = kHexChars[ ( offset >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 4 ) & 0xF ];
+ *s++ = kHexChars[ offset & 0xF ];
+ }
+ if( s != prefixString )
+ {
+ *s++ = ':';
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) );
+ *s = '\0';
+
+ // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read.
+ // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up).
+
+ s = hexString;
+ chunkSize = ( inDataSize < 16 ) ? inDataSize : 16;
+ n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16;
+ for( i = 0; i < n; ++i )
+ {
+ if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( i < chunkSize )
+ {
+ *s++ = kHexChars[ src[ i ] >> 4 ];
+ *s++ = kHexChars[ src[ i ] & 0xF ];
+ }
+ else
+ {
+ *s++ = ' ';
+ *s++ = ' ';
+ }
+ }
+ check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) );
+ *s = '\0';
+
+ // Build a string with the ASCII version of the data (replaces non-printable characters with '^').
+ // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up).
+
+ s = asciiString;
+ if( !( inFlags & kDebugFlagsNoASCII ) )
+ {
+ *s++ = ' ';
+ *s++ = '|';
+ for( i = 0; i < n; ++i )
+ {
+ if( i < chunkSize )
+ {
+ c = src[ i ];
+ if( !DebugIsPrint( c ) )
+ {
+ c = '^';
+ }
+ }
+ else
+ {
+ c = '`';
+ }
+ *s++ = (char) c;
+ }
+ *s++ = '|';
+ check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) );
+ }
+ *s = '\0';
+
+ // Build a string indicating how bytes are in the hex dump. Only printed on the first line.
+
+ s = byteCountString;
+ if( !( inFlags & kDebugFlagsNoByteCount ) )
+ {
+ if( src == start )
+ {
+ s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize );
+ }
+ }
+ check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) );
+ *s = '\0';
+
+ // Build the entire line from all the pieces we've previously built.
+
+ if( outBuffer )
+ {
+ if( src == start )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel ? inLabel : "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+ else
+ {
+ if( src == start )
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel,
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+
+ // Move to the next chunk. Exit if there is no more data.
+
+ offset += (int) chunkSize;
+ src += chunkSize;
+ inDataSize -= chunkSize;
+ if( inDataSize == 0 )
+ {
+ break;
+ }
+ }
+
+ // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative.
+
+ return( (size_t)( dst - outBuffer ) );
+}
+
+//===========================================================================================================================
+// DebugNumVersionToString
+//===========================================================================================================================
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString )
+{
+ char * s;
+ uint8_t majorRev;
+ uint8_t minor;
+ uint8_t bugFix;
+ uint8_t stage;
+ uint8_t revision;
+
+ check( inString );
+
+ majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF );
+ minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F );
+ bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F );
+ stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF );
+ revision = (uint8_t)( inVersion & 0xFF );
+
+ // Convert the major, minor, and bugfix numbers.
+
+ s = inString;
+ s += sprintf( s, "%u", majorRev );
+ s += sprintf( s, ".%u", minor );
+ if( bugFix != 0 )
+ {
+ s += sprintf( s, ".%u", bugFix );
+ }
+
+ // Convert the version stage and non-release revision number.
+
+ switch( stage )
+ {
+ case kVersionStageDevelopment:
+ s += sprintf( s, "d%u", revision );
+ break;
+
+ case kVersionStageAlpha:
+ s += sprintf( s, "a%u", revision );
+ break;
+
+ case kVersionStageBeta:
+ s += sprintf( s, "b%u", revision );
+ break;
+
+ case kVersionStageFinal:
+
+ // A non-release revision of zero is a special case indicating the software is GM (at the golden master
+ // stage) and therefore, the non-release revision should not be added to the string.
+
+ if( revision != 0 )
+ {
+ s += sprintf( s, "f%u", revision );
+ }
+ break;
+
+ default:
+ dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage );
+ break;
+ }
+ return( inString );
+}
+
+//===========================================================================================================================
+// DebugTaskLevel
+//===========================================================================================================================
+
+DEBUG_EXPORT uint32_t DebugTaskLevel( void )
+{
+ uint32_t level;
+
+ level = 0;
+
+#if( TARGET_OS_VXWORKS )
+ if( intContext() )
+ {
+ level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask );
+ }
+#endif
+
+ return( level );
+}
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWinEnableConsole
+//===========================================================================================================================
+
+#pragma warning( disable:4311 )
+
+static void DebugWinEnableConsole( void )
+{
+ static bool sConsoleEnabled = false;
+ BOOL result;
+ int fileHandle;
+ FILE * file;
+ int err;
+
+ if( sConsoleEnabled )
+ {
+ goto exit;
+ }
+
+ // Create console window.
+
+ result = AllocConsole();
+ require_quiet( result, exit );
+
+ // Redirect stdin to the console stdin.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "r", stdin );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "r" );
+ require_quiet( file, exit );
+
+ *stdin = *file;
+ #endif
+
+ err = setvbuf( stdin, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stdout to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stdout );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stdout = *file;
+ #endif
+
+ err = setvbuf( stdout, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stderr to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stderr );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stderr = *file;
+ #endif
+
+ err = setvbuf( stderr, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ sConsoleEnabled = true;
+
+exit:
+ return;
+}
+
+#pragma warning( default:4311 )
+
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWinCharToTCharString
+//===========================================================================================================================
+
+static TCHAR *
+ DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount )
+{
+ const char * src;
+ TCHAR * dst;
+ TCHAR * end;
+
+ if( inCharCount == kSizeCString )
+ {
+ inCharCount = strlen( inCharString );
+ }
+ src = inCharString;
+ dst = outTCharString;
+ if( inTCharCountMax > 0 )
+ {
+ inTCharCountMax -= 1;
+ if( inTCharCountMax > inCharCount )
+ {
+ inTCharCountMax = inCharCount;
+ }
+
+ end = dst + inTCharCountMax;
+ while( dst < end )
+ {
+ *dst++ = (TCHAR) *src++;
+ }
+ *dst = 0;
+ }
+ if( outTCharCount )
+ {
+ *outTCharCount = (size_t)( dst - outTCharString );
+ }
+ return( outTCharString );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Debugging ==
+#endif
+
+//===========================================================================================================================
+// DebugServicesTest
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugServicesTest( void )
+{
+ OSStatus err;
+ char s[ 512 ];
+ uint8_t * p;
+ uint8_t data[] =
+ {
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0xAA,
+ 0xBB, 0xCC, 0xDD,
+ 0xEE,
+ 0xFF,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0,
+ 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1
+ };
+
+ debug_initialize( kDebugOutputTypeMetaConsole );
+
+ // check's
+
+ check( 0 && "SHOULD SEE: check" );
+ check( 1 && "SHOULD *NOT* SEE: check (valid)" );
+ check_string( 0, "SHOULD SEE: check_string" );
+ check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" );
+ check_noerr( -123 );
+ check_noerr( 10038 );
+ check_noerr( 22 );
+ check_noerr( 0 );
+ check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" );
+ check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" );
+ check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 );
+
+ // require's
+
+ require( 0 && "SHOULD SEE", require1 );
+ { err = kResponseErr; goto exit; }
+require1:
+ require( 1 && "SHOULD *NOT* SEE", require2 );
+ goto require2Good;
+require2:
+ { err = kResponseErr; goto exit; }
+require2Good:
+ require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" );
+ { err = kResponseErr; goto exit; }
+require3:
+ require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" );
+ goto require4Good;
+require4:
+ { err = kResponseErr; goto exit; }
+require4Good:
+ require_quiet( 0 && "SHOULD SEE", require5 );
+ { err = kResponseErr; goto exit; }
+require5:
+ require_quiet( 1 && "SHOULD *NOT* SEE", require6 );
+ goto require6Good;
+require6:
+ { err = kResponseErr; goto exit; }
+require6Good:
+ require_noerr( -1, require7 );
+ { err = kResponseErr; goto exit; }
+require7:
+ require_noerr( 0, require8 );
+ goto require8Good;
+require8:
+ { err = kResponseErr; goto exit; }
+require8Good:
+ require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string");
+ { err = kResponseErr; goto exit; }
+require9:
+ require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" );
+ goto require10Good;
+require10:
+ { err = kResponseErr; goto exit; }
+require10Good:
+ require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" );
+ { err = kResponseErr; goto exit; }
+require11:
+ require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" );
+ goto require12Good;
+require12:
+ { err = kResponseErr; goto exit; }
+require12Good:
+ require_noerr_quiet( -4, require13 );
+ { err = kResponseErr; goto exit; }
+require13:
+ require_noerr_quiet( 0, require14 );
+ goto require14Good;
+require14:
+ { err = kResponseErr; goto exit; }
+require14Good:
+ require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require15:
+ require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) );
+ goto require16Good;
+require16:
+ { err = kResponseErr; goto exit; }
+require16Good:
+ require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require17:
+ require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) );
+ goto require18Good;
+require18:
+ { err = kResponseErr; goto exit; }
+require18Good:
+ require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require19:
+ require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) );
+ goto require20Good;
+require20:
+ { err = kResponseErr; goto exit; }
+require20Good:
+ require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require21:
+ require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) );
+ goto require22Good;
+require22:
+ { err = kResponseErr; goto exit; }
+require22Good:
+ require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" );
+ { err = kResponseErr; goto exit; }
+require23:
+ require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" );
+ goto require24Good;
+require24:
+ { err = kResponseErr; goto exit; }
+require24Good:
+
+#if( defined( __MWERKS__ ) )
+ #if( defined( __cplusplus ) && __option( exceptions ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#else
+ #if( defined( __cplusplus ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#endif
+
+#if( COMPILER_HAS_EXCEPTIONS )
+ try
+ {
+ require_throw( 1 && "SHOULD *NOT* SEE" );
+ require_throw( 0 && "SHOULD SEE" );
+ }
+ catch( ... )
+ {
+ goto require26Good;
+ }
+ { err = kResponseErr; goto exit; }
+require26Good:
+#endif
+
+ // translate_errno
+
+ err = translate_errno( 1 != -1, -123, -567 );
+ require( ( err == 0 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, -123, -567 );
+ require( ( err == -123 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, 0, -567 );
+ require( ( err == -567 ) && "SHOULD *NOT* SEE", exit );
+
+ // debug_string
+
+ debug_string( "debug_string" );
+
+ // DebugSNPrintF
+
+ DebugSNPrintF( s, sizeof( s ), "%d", 1234 );
+ require_action( strcmp( s, "1234" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 );
+ require_action( strcmp( s, "2345" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" );
+ require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) );
+ require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) );
+ require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 );
+
+ #if( TYPE_LONGLONG_NATIVE )
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) );
+ require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) );
+ require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) );
+ require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) );
+ require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd'
+ require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 );
+
+ #if( defined( MDNS_DEBUGMSGS ) )
+ {
+ mDNSAddr maddr;
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv4;
+ maddr.ip.v4.b[ 0 ] = 127;
+ maddr.ip.v4.b[ 1 ] = 0;
+ maddr.ip.v4.b[ 2 ] = 0;
+ maddr.ip.v4.b[ 3 ] = 1;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 );
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv6;
+ maddr.ip.v6.b[ 0 ] = 0xFE;
+ maddr.ip.v6.b[ 1 ] = 0x80;
+ maddr.ip.v6.b[ 15 ] = 0x01;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if( AF_INET )
+ {
+ struct sockaddr_in sa4;
+
+ memset( &sa4, 0, sizeof( sa4 ) );
+ sa4.sin_family = AF_INET;
+ p = (uint8_t *) &sa4.sin_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ p = (uint8_t *) &sa4.sin_addr.s_addr;
+ p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF );
+ p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF );
+ p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF );
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 );
+ require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if( AF_INET6 )
+ {
+ struct sockaddr_in6 sa6;
+
+ memset( &sa6, 0, sizeof( sa6 ) );
+ sa6.sin6_family = AF_INET6;
+ p = (uint8_t *) &sa6.sin6_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ sa6.sin6_addr.s6_addr[ 0 ] = 0xFE;
+ sa6.sin6_addr.s6_addr[ 1 ] = 0x80;
+ sa6.sin6_addr.s6_addr[ 15 ] = 0x01;
+ sa6.sin6_scope_id = 2;
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 );
+ require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ // Unicode
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ #if( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ #if( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ // Misc
+
+ DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" );
+ require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%m", 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"",
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8",
+ 32, 32 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ // Hex Dumps
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd'
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount,
+ s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine |
+ kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ // dlog's
+
+ dlog( kDebugLevelNotice, "dlog\n" );
+ dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 );
+ dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" );
+ dlogmem( kDebugLevelNotice, data, sizeof( data ) );
+
+ // Done
+
+ DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" );
+ err = kNoErr;
+
+exit:
+ if( err )
+ {
+ DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" );
+ }
+ return( err );
+}
+
+#endif // DEBUG
diff --git a/src/libs/zeroconf/embed/DebugServices.h b/src/libs/zeroconf/embed/DebugServices.h
new file mode 100644
index 0000000000..d4e5c7203d
--- /dev/null
+++ b/src/libs/zeroconf/embed/DebugServices.h
@@ -0,0 +1,1607 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header DebugServices
+
+ Debugging Library
+*/
+
+#ifndef __DEBUG_SERVICES__
+#define __DEBUG_SERVICES__
+
+#include <stdarg.h>
+
+#include "CommonServices.h"
+
+#if( TARGET_OS_VXWORKS )
+ #include "logLib.h"
+#endif
+
+#if 0
+#pragma mark == Settings ==
+#endif
+
+//===========================================================================================================================
+// Settings
+//===========================================================================================================================
+
+// General
+
+#if( !defined( DEBUG ) )
+ #define DEBUG 0
+#endif
+
+#if( defined( NDEBUG ) && DEBUG )
+ #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync
+#endif
+
+// AssertMacros.h/Debugging.h overrides.
+
+#if( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) )
+ #define DEBUG_OVERRIDE_APPLE_MACROS 1
+#endif
+
+// Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything).
+
+#if( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) )
+ #define __ROUTINE__ __func__
+#elif( defined( __GNUC__ ) )
+ #define __ROUTINE__ __PRETTY_FUNCTION__
+#elif( defined( _MSC_VER ) && !defined( _WIN32_WCE ) )
+ #define __ROUTINE__ __FUNCTION__
+#else
+ #define __ROUTINE__ ""
+#endif
+
+// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing.
+
+#if( defined( __GNUC__ ) )
+ #if( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) )
+ #define DEBUG_C99_VA_ARGS 1
+ #define DEBUG_GNU_VA_ARGS 0
+ #else
+ #define DEBUG_C99_VA_ARGS 0
+ #define DEBUG_GNU_VA_ARGS 1
+ #endif
+#elif( defined( __MWERKS__ ) )
+ #define DEBUG_C99_VA_ARGS 1
+ #define DEBUG_GNU_VA_ARGS 0
+#else
+ #define DEBUG_C99_VA_ARGS 0
+ #define DEBUG_GNU_VA_ARGS 0
+#endif
+
+#if 0
+#pragma mark == Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_FPRINTF_ENABLED
+
+ @abstract Enables ANSI C fprintf output.
+*/
+
+#if( !defined( DEBUG_FPRINTF_ENABLED ) )
+ #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+ #define DEBUG_FPRINTF_ENABLED 1
+ #else
+ #define DEBUG_FPRINTF_ENABLED 0
+ #endif
+#else
+ #if( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE )
+ #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_MAC_OS_X_IOLOG_ENABLED
+
+ @abstract Enables IOLog (Mac OS X Kernel) output.
+*/
+
+#if( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) )
+ #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_KPRINTF_ENABLED
+
+ @abstract Enables kprintf (Mac OS X Kernel) output.
+*/
+
+#if( !defined( DEBUG_KPRINTF_ENABLED ) )
+ #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_IDEBUG_ENABLED
+
+ @abstract Enables iDebug (Mac OS X user and Kernel) output.
+
+ @discussion
+
+ For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence
+ of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies
+ on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug.
+*/
+
+#if( !defined( DEBUG_IDEBUG_ENABLED ) )
+ #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_CORE_SERVICE_ASSERTS_ENABLED
+
+ @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework.
+*/
+
+#if( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) )
+ #if( defined( __DEBUGGING__ ) )
+ #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1
+ #else
+ #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputType
+
+ @abstract Type of debug output (i.e. where the output goes).
+*/
+
+typedef uint32_t DebugOutputType;
+
+#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params
+#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context
+#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename]
+#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params
+#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params
+#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params
+#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params
+#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params
+#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL.
+
+// Console meta output kind - Any kind of Console output (in horizontal order of preference):
+//
+// Mac OS X = ANSI printf (viewable in Console.app)
+// Mac OS X Kernel = IOLog (/var/log/system.log) or kprintf (serial).
+// Windows = ANSI printf (Console window) or OutputDebugString (debugger).
+// Other = ANSI printf (viewer varies).
+
+#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputTypeFlags
+
+ @abstract Flags controlling how the output type is configured.
+
+ @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.).
+ @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout.
+ @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr.
+ @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg).
+*/
+
+typedef unsigned int DebugOutputTypeFlags;
+
+#define kDebugOutputTypeFlagsTypeMask 0xF
+#define kDebugOutputTypeFlagsStdOut 1
+#define kDebugOutputTypeFlagsStdErr 2
+#define kDebugOutputTypeFlagsFile 10
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputFunctionPtr
+
+ @abstract Function ptr for a custom callback to print debug output.
+*/
+
+typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext );
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#if 0
+#pragma mark == Flags ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugFlags
+
+ @abstract Flags controlling how output is printed.
+*/
+
+typedef uint32_t DebugFlags;
+
+#define kDebugFlagsNone 0
+#define kDebugFlagsNoAddress ( 1 << 0 )
+#define kDebugFlagsNoOffset ( 1 << 1 )
+#define kDebugFlags32BitOffset ( 1 << 2 )
+#define kDebugFlagsNoASCII ( 1 << 3 )
+#define kDebugFlagsNoNewLine ( 1 << 4 )
+#define kDebugFlags8BitSeparator ( 1 << 5 )
+#define kDebugFlags16BitSeparator ( 1 << 6 )
+#define kDebugFlagsNo32BitSeparator ( 1 << 7 )
+#define kDebugFlagsNo16ByteHexPad ( 1 << 8 )
+#define kDebugFlagsNoByteCount ( 1 << 9 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DebugTaskLevelFlags
+
+ @abstract Flags indicating the task level.
+*/
+
+enum
+{
+ kDebugInterruptLevelShift = 0,
+ kDebugInterruptLevelMask = 0x00000007,
+ kDebugInVBLTaskMask = 0x00000010,
+ kDebugInDeferredTaskMask = 0x00000020,
+ kDebugInSecondaryInterruptHandlerMask = 0x00000040,
+ kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h.
+ kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h.
+ kDebugInterruptDepthShift = 16,
+ kDebugInterruptDepthMask = 0x00FF0000
+};
+
+#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \
+ ( ( ( LEVEL ) & kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift )
+
+#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \
+ ( ( ( LEVEL ) & kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift )
+
+#if 0
+#pragma mark == Levels ==
+#endif
+
+//===========================================================================================================================
+// Constants & Types - Levels
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugLevel
+
+ @abstract Level used to control debug logging.
+*/
+
+typedef int32_t DebugLevel;
+
+// Levels
+
+#define kDebugLevelMask 0x0000FFFF
+#define kDebugLevelChatty 100
+#define kDebugLevelVerbose 500
+#define kDebugLevelTrace 800
+#define kDebugLevelInfo 1000
+#define kDebugLevelNotice 3000
+#define kDebugLevelWarning 5000
+#define kDebugLevelAssert 6000
+#define kDebugLevelRequire 7000
+#define kDebugLevelError 8000
+#define kDebugLevelCritical 9000
+#define kDebugLevelAlert 10000
+#define kDebugLevelEmergency 11000
+#define kDebugLevelTragic 12000
+#define kDebugLevelMax 0x0000FFFF
+
+// Level Flags
+
+#define kDebugLevelFlagMask 0xFFFF0000
+#define kDebugLevelFlagStackTrace 0x00010000
+#define kDebugLevelFlagDebugBreak 0x00020000
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef LogLevel
+
+ @abstract Level used to control which events are logged.
+*/
+
+typedef int32_t LogLevel;
+
+#define kLogLevelUninitialized -1L
+#define kLogLevelAll 0L
+#define kLogLevelChatty 100L
+#define kLogLevelVerbose 500L
+#define kLogLevelTrace 800L
+#define kLogLevelInfo 1000L
+#define kLogLevelNotice 3000L
+#define kLogLevelWarning 4000L
+#define kLogLevelAssert 6000L
+#define kLogLevelRequire 7000L
+#define kLogLevelError 8000L
+#define kLogLevelCritical 9000L
+#define kLogLevelAlert 10000L
+#define kLogLevelEmergency 11000L
+#define kLogLevelTragic 12000L
+#define kLogLevelOff 0x0000FFFEL
+
+#if 0
+#pragma mark == Properties ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugPropertyTag
+
+ @abstract Tag for properties.
+*/
+
+typedef uint32_t DebugPropertyTag;
+
+#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+
+#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin
+
+#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+
+#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+#if 0
+#pragma mark == General macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_UNUSED
+
+ @abstract Macro to mark a paramter as unused to avoid unused parameter warnings.
+
+ @discussion
+
+ There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us
+ indicate a variable is unused in a manner that is supported by most compilers.
+*/
+
+#define DEBUG_UNUSED( X ) (void)( X )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_USE_ONLY
+
+ @abstract Macro to mark a variable as used only when debugging is enabled.
+
+ @discussion
+
+ Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate
+ compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that
+ are only used for debugging.
+*/
+
+#if( DEBUG )
+ #define DEBUG_USE_ONLY( X )
+#else
+ #define DEBUG_USE_ONLY( X ) (void)( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_LOCAL
+
+ @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+
+ @discussion
+
+ Rather than using "static" directly, using this macros allows you to access these variables external while
+ debugging without being penalized for production builds.
+*/
+
+#if( DEBUG )
+ #define DEBUG_LOCAL
+#else
+ #define DEBUG_LOCAL static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_STATIC
+
+ @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+
+ @discussion
+
+ Rather than using "static" directly, using this macros allows you to access these variables external while
+ debugging without being penalized for production builds.
+*/
+
+#if( DEBUG )
+ #define DEBUG_STATIC
+#else
+ #define DEBUG_STATIC static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_EXPORT
+
+ @abstract Macros to export variables.
+
+ @discussion
+
+ "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but
+ // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not
+ // solve the problem of multiple drivers in the same dependency chain since they share symbols.
+*/
+
+#if( TARGET_API_MAC_OSX_KERNEL )
+ #define DEBUG_EXPORT __private_extern__
+#else
+ #define DEBUG_EXPORT extern
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_add
+
+ @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off.
+*/
+
+#if( DEBUG )
+ #define debug_add( A, B ) ( A ) += ( B )
+#else
+ #define debug_add( A, B )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_perform
+
+ @abstract Macro to perform something in debug-only builds.
+*/
+
+#if( DEBUG )
+ #define debug_perform( X ) do { X; } while( 0 )
+#else
+ #define debug_perform( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function translate_errno
+
+ @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error.
+*/
+
+#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) )
+
+#if 0
+#pragma mark == Compile Time macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_compile_time
+
+ @abstract Performs a compile-time check of something such as the size of an int.
+
+ @discussion
+
+ This declares an array with a size that is determined by a compile-time expression. If the expression evaluates
+ to 0, the array has a size of -1, which is illegal and generates a compile-time error.
+
+ For example:
+
+ check_compile_time( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This only works in places where extern declarations are allowed (e.g. global scope).
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+
+ Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not
+ work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable.
+*/
+
+#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ]
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_compile_time_code
+
+ @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int.
+
+ @discussion
+
+ This creates a switch statement with an existing case for 0 and an additional case using the result of a
+ compile-time expression. A switch statement cannot have two case labels with the same constant so if the
+ compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time
+ expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error.
+
+ For example:
+
+ check_compile_time_code( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This does not work in a global scope so it must be inside a function.
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+*/
+
+#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; }
+
+#if 0
+#pragma mark == check macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check
+
+ @abstract Check that an expression is true (non-zero).
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method.
+
+ Code inside check() statements is not compiled into production builds.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check
+#endif
+#if( !defined( check ) )
+ #if( DEBUG )
+ #define check( X ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ } while( 0 )
+ #else
+ #define check( X )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_string
+
+ @abstract Check that an expression is true (non-zero) with an explanation.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method.
+
+ Code inside check_string() statements is not compiled into production builds.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_string
+#endif
+#if( !defined( check_string ) )
+ #if( DEBUG )
+ #define check_string( X, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_string( X, STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_noerr
+
+ @abstract Check that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method.
+
+ Code inside check_noerr() statements is not compiled into production builds.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_noerr
+#endif
+#if( !defined( check_noerr ) )
+ #if( DEBUG )
+ #define check_noerr( ERR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_noerr( ERR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_noerr_string
+
+ @abstract Check that an error code is noErr (0) with an explanation.
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method.
+
+ Code inside check_noerr_string() statements is not compiled into production builds.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_noerr_string
+#endif
+#if( !defined( check_noerr_string ) )
+ #if( DEBUG )
+ #define check_noerr_string( ERR, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_noerr_string( ERR, STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_translated_errno
+
+ @abstract Check a condition and prints errno (if non-zero) to the log.
+
+ @discussion
+
+ Code inside check_translated_errno() statements is not compiled into production builds.
+*/
+
+#if( !defined( check_translated_errno ) )
+ #if( DEBUG )
+ #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \
+ do \
+ { \
+ if( !( TEST ) ) \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERRNO ); \
+ localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \
+ debug_print_assert( localErr, #TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_ptr_overlap
+
+ @abstract Checks that two ptrs do not overlap.
+*/
+
+#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \
+ do \
+ { \
+ check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \
+ ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \
+ check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \
+ ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \
+ \
+ } while( 0 )
+
+#if 0
+#pragma mark == require macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require
+
+ @abstract Requires that an expression evaluate to true.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require
+#endif
+#if( !defined( require ) )
+ #define require( X, LABEL ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_string
+
+ @abstract Requires that an expression evaluate to true with an explanation.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_string
+#endif
+#if( !defined( require_string ) )
+ #define require_string( X, LABEL, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_quiet
+
+ @abstract Requires that an expression evaluate to true.
+
+ @discussion
+
+ If expression evalulates to false, this jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_quiet
+#endif
+#if( !defined( require_quiet ) )
+ #define require_quiet( X, LABEL ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr
+#endif
+#if( !defined( require_noerr ) )
+ #define require_noerr( ERR, LABEL ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_string
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.), and a custom explanation string using the default debugging output method using the
+ default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_string
+#endif
+#if( !defined( require_noerr_string ) )
+ #define require_noerr_string( ERR, LABEL, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action_string
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.), and a custom explanation string using the default debugging output method using the
+ default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action_string
+#endif
+#if( !defined( require_noerr_action_string ) )
+ #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_quiet
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_quiet
+#endif
+#if( !defined( require_noerr_quiet ) )
+ #define require_noerr_quiet( ERR, LABEL ) \
+ do \
+ { \
+ if( ( ERR ) != 0 ) \
+ { \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action
+
+ @abstract Require that an error code is noErr (0) with an action to execute otherwise.
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action
+#endif
+#if( !defined( require_noerr_action ) )
+ #define require_noerr_action( ERR, LABEL, ACTION ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action_quiet
+
+ @abstract Require that an error code is noErr (0) with an action to execute otherwise.
+
+ @discussion
+
+ If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action_quiet
+#endif
+#if( !defined( require_noerr_action_quiet ) )
+ #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \
+ do \
+ { \
+ if( ( ERR ) != 0 ) \
+ { \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action
+
+ @abstract Requires that an expression evaluate to true with an action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action
+#endif
+#if( !defined( require_action ) )
+ #define require_action( X, LABEL, ACTION ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action_quiet
+
+ @abstract Requires that an expression evaluate to true with an action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action_quiet
+#endif
+#if( !defined( require_action_quiet ) )
+ #define require_action_quiet( X, LABEL, ACTION ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action_string
+
+ @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method then executes an
+ action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action_string
+#endif
+#if( !defined( require_action_string ) )
+ #define require_action_string( X, LABEL, ACTION, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_throw
+
+ @abstract Requires that an expression evaluates to true or an exception is thrown.
+
+ @discussion
+
+ If the expression evaluates to false, this prints debugging information (actual expression string, file,
+ line number, function name, etc.) using the default debugging output method then throws an exception.
+*/
+
+#if( defined( __cplusplus ) )
+ #define require_throw( X ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ throw kUnknownErr; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+#if 0
+#pragma mark == Design-By-Contract macros ==
+#endif
+
+//===========================================================================================================================
+// Design-By-Contract macros
+//===========================================================================================================================
+
+#define ensure( X ) check( X )
+#define ensure_string( X, STR ) check_string( X, STR )
+#define ensure_noerr( ERR ) check_noerr( ERR )
+#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR )
+#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+
+// Note: Design-By-Contract "require" macros are already defined elsewhere.
+
+#if 0
+#pragma mark == Expect macros ==
+#endif
+
+//===========================================================================================================================
+// Expect macros
+//===========================================================================================================================
+
+// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal
+// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly
+// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can
+// also be useful to measure the cost of error checking code by profiling with it enable and with it disabled.
+
+#if( DEBUG_EXPECT_VERIFIED )
+ #define require_expect
+ #define require_string_expect
+ #define require_quiet_expect
+ #define require_noerr_expect
+ #define require_noerr_string_expect
+ #define require_noerr_action_string_expect
+ #define require_noerr_quiet_expect
+ #define require_noerr_action_expect
+ #define require_noerr_action_quiet_expect
+ #define require_action_expect
+ #define require_action_quiet_expect
+ #define require_action_string_expect
+#else
+ #define require_expect require
+ #define require_string_expect require_string
+ #define require_quiet_expect require_quiet
+ #define require_noerr_expect require_noerr
+ #define require_noerr_string_expect require_noerr_string
+ #define require_noerr_action_string_expect require_noerr_action_string
+ #define require_noerr_quiet_expect require_noerr_quiet
+ #define require_noerr_action_expect require_noerr_action
+ #define require_noerr_action_quiet_expect require_noerr_action_quiet
+ #define require_action_expect require_action
+ #define require_action_quiet_expect require_action_quiet
+ #define require_action_string_expect require_action_string
+#endif
+
+#if 0
+#pragma mark == Output macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_string
+
+ @abstract Prints a debugging C string.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef debug_string
+#endif
+#if( !defined( debug_string ) )
+ #if( DEBUG )
+ #define debug_string( STR ) \
+ do \
+ { \
+ debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ \
+ } while( 0 )
+ #else
+ #define debug_string( STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_print_assert
+
+ @abstract Prints an assertion.
+*/
+
+#if( DEBUG )
+ #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \
+ DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#else
+ #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlog
+
+ @abstract Prints a debug-only message.
+*/
+
+#if( DEBUG )
+ #if( DEBUG_C99_VA_ARGS )
+ #define dlog( ... ) DebugPrintF( __VA_ARGS__ )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define dlog( ARGS... ) DebugPrintF( ## ARGS )
+ #else
+ #define dlog DebugPrintF
+ #endif
+#else
+ #if( DEBUG_C99_VA_ARGS )
+ #define dlog( ... )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define dlog( ARGS... )
+ #else
+ #define dlog while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlogv
+
+ @abstract Prints a debug-only message.
+*/
+
+#if( DEBUG )
+ #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) )
+#else
+ #define dlogv( LEVEL, FORMAT, LIST )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlogmem
+
+ @abstract Prints a debug-only dump of memory.
+*/
+
+#if( DEBUG )
+ #define dlogmem( LEVEL, PTR, SIZE ) \
+ DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 )
+#else
+ #define dlogmem( LEVEL, PTR, SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DebugNSLog
+
+ @abstract Debug-only macro for the Cocoa NSLog function.
+*/
+
+#if( DEBUG )
+ #if( DEBUG_C99_VA_ARGS )
+ #define DebugNSLog( ... ) NSLog( __VA_ARGS__ )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define DebugNSLog( ARGS... ) NSLog( ## ARGS )
+ #else
+ #define DebugNSLog NSLog
+ #endif
+#else
+ #if( DEBUG_C99_VA_ARGS )
+ #define DebugNSLog( ... )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define DebugNSLog( ARGS... )
+ #else
+ #define DebugNSLog while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DebugLogMsg
+
+ @abstract Debug-only macro for the VxWorks logMsg function.
+*/
+
+#if( TARGET_OS_VXWORKS )
+ #if( DEBUG )
+ #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \
+ do \
+ { \
+ if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \
+ { \
+ logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 )
+ #endif
+#else
+ #define DebugLogMsg dlog
+#endif
+
+#if 0
+#pragma mark == Routines - General ==
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugInitialize
+
+ @abstract Initializes the debugging library for a specific kind of output.
+
+ @param inType
+ @param varArg Variable number parameters, controlled by the "inType" parameter.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... );
+#endif
+
+#if( DEBUG )
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_initialize( ... ) DebugInitialize( __VA_ARGS__ )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS )
+ #else
+ #define debug_initialize DebugInitialize
+ #endif
+#else
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_initialize( ... )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_initialize( ARGS... )
+ #else
+ #define debug_initialize while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugFinalize
+
+ @abstract Releases any resources used by the debugging library
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT void DebugFinalize( void );
+#endif
+
+#if( DEBUG )
+ #define debug_terminate() DebugFinalize()
+#else
+ #define debug_terminate()
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugGetProperty
+
+ @abstract Gets the specified property from the debugging library.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if( DEBUG )
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_get_property( ... ) DebugGetProperty( __VA_ARGS__ )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS )
+ #else
+ #define debug_get_property DebugGetProperty
+ #endif
+#else
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_get_property( ... )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_get_property( ARGS... )
+ #else
+ #define debug_get_property while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSetProperty
+
+ @abstract Sets the specified property from the debugging library.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if( DEBUG )
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_set_property( ... ) DebugSetProperty( __VA_ARGS__ )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS )
+ #else
+ #define debug_set_property DebugSetProperty
+ #endif
+#else
+ #if( DEBUG_C99_VA_ARGS )
+ #define debug_set_property( ... )
+ #elif( DEBUG_GNU_VA_ARGS )
+ #define debug_set_property( ARGS... )
+ #else
+ #define debug_set_property while( 0 )
+ #endif
+#endif
+
+#if 0
+#pragma mark == Routines - Debugging Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintF
+
+ @abstract Prints a debug message with printf-style formatting.
+
+ @param inLevel Error that generated this assert or noErr.
+
+ @param inFormatString
+ C string containing assertion text.
+
+ @param VAR_ARG
+ Variable number of arguments depending on the format string.
+
+ @result Number of bytes printed or -1 on error.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintFVAList
+
+ @abstract va_list version of DebugPrintF. See DebugPrintF for more info.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintAssert
+
+ @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message,
+ an optional source filename, an optional source line number.
+
+ @param inErrorCode Error that generated this assert or noErr.
+ @param inAssertString C string containing assertion text.
+ @param inMessage C string containing a message about the assert.
+ @param inFileName C string containing path of file where the error occurred.
+ @param inLineNumber Line number in source file where the error occurred.
+ @param inFunction C string containing name of function where assert occurred.
+
+ @discussion
+
+ Example output:
+
+ [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed
+ [ASSERT] where: "MyFile.c", line 123, ("MyFunction")
+
+ OR
+
+ [ASSERT] error: -6728 (kNoMemoryErr)
+ [ASSERT] where: "MyFile.c", line 123, ("MyFunction")
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT void
+ DebugPrintAssert(
+ int_least32_t inErrorCode,
+ const char * inAssertString,
+ const char * inMessage,
+ const char * inFilename,
+ int_least32_t inLineNumber,
+ const char * inFunction );
+#endif
+
+#if 0
+#pragma mark == Routines - Utilities ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSNPrintF
+
+ @abstract Debugging versions of standard C snprintf with extra features.
+
+ @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0.
+ @param buflen Size of the buffer including space for the null terminator.
+ @param fmt printf-style format string.
+ @param VAR_ARG Variable number of arguments depending on the format string.
+
+ @result Number of characters written (minus the null terminator).
+
+ @discussion
+
+ Extra features over the standard C snprintf:
+ <pre>
+ 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+ %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+ %a - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+ %#a - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+ %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+ %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+ %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+ %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+ %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+ %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+ %#s - Pascal-style length-prefixed string. Arg=ptr to string.
+ %##s - DNS label-sequence name. Arg=ptr to name.
+ %S - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+ %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+ %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+ %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+ </pre>
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSNPrintFVAList
+
+ @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugGetErrorString
+
+ @abstract Gets an error string from an error code.
+
+ @param inStatus Error code to get the string for.
+ @param inBuffer Optional buffer to copy the string to for non-static strings. May be null.
+ @param inBufferSize Size of optional buffer. May be 0.
+
+ @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a
+ buffer is supplied, the return value will always be a pointer to the supplied buffer, which will
+ contain the best available description of the error code. If a buffer is not supplied, the return
+ value will be the best available description of the error code that can be represented as a static
+ string. This allows code that cannot use a temporary buffer to hold the result to still get a useful
+ error string in most cases, but also allows code that can use a temporary buffer to get the best
+ available description.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugHexDump
+
+ @abstract Hex dumps data to a string or to the output device.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT size_t
+ DebugHexDump(
+ DebugLevel inLevel,
+ int inIndent,
+ const char * inLabel,
+ size_t inLabelSize,
+ int inLabelMinWidth,
+ const char * inType,
+ size_t inTypeSize,
+ const void * inDataStart,
+ const void * inData,
+ size_t inDataSize,
+ DebugFlags inFlags,
+ char * outBuffer,
+ size_t inBufferSize );
+#endif
+
+#if( DEBUG )
+ #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \
+ DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \
+ ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) )
+#else
+ #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugTaskLevel
+
+ @abstract Returns the current task level.
+
+ @result Current task level
+
+ @discussion
+
+ Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify):
+ <pre>
+ kDebugInterruptLevelMask - Indicates the current interrupt level (> 0 means interrupt time).
+ kDebugInVBLTaskMask - Indicates if a VBL task is currently being executed.
+ kDebugInDeferredTaskMask - Indicates if a Deferred Task is currently being executed.
+ kDebugInSecondaryInterruptHandlerMask - Indicates if a Secondary Interrupt Handler is currently being executed.
+ kDebugPageFaultFatalMask - Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+ kDebugMPTaskLevelMask - Indicates if being called from an MP task.
+ kDebugInterruptDepthMask - 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+ </pre>
+
+ Helpers:
+ <pre>
+ DebugExtractTaskLevelInterruptDepth() - Macro to extract interrupt depth from task level value.
+ </pre>
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT uint32_t DebugTaskLevel( void );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugServicesTest
+
+ @abstract Unit test.
+*/
+
+#if( DEBUG )
+ DEBUG_EXPORT OSStatus DebugServicesTest( void );
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __DEBUG_SERVICES__
diff --git a/src/libs/zeroconf/embed/dnssd_clientlib.c b/src/libs/zeroconf/embed/dnssd_clientlib.c
index 2a5dd956ab..6bd7465c34 100644
--- a/src/libs/zeroconf/embed/dnssd_clientlib.c
+++ b/src/libs/zeroconf/embed/dnssd_clientlib.c
@@ -29,7 +29,7 @@
#include <stdlib.h>
#include <string.h>
-#include "dns_sd_types.h"
+#include "../dns_sd_types.h"
#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
//#pragma export on
@@ -49,7 +49,7 @@
*********************************************************************************************/
namespace ZeroConf { namespace embeddedLib {
-#include "dns_sd_funct.h"
+#include "../dns_sd_funct.h"
#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9')
diff --git a/src/libs/zeroconf/embed/dnssd_clientstub.c b/src/libs/zeroconf/embed/dnssd_clientstub.c
index 06a64a8bc2..b09fc414e4 100644
--- a/src/libs/zeroconf/embed/dnssd_clientstub.c
+++ b/src/libs/zeroconf/embed/dnssd_clientstub.c
@@ -35,22 +35,23 @@
#include "dnssd_ipc.h"
-namespace ZeroConf { namespace embeddedLib {
-#include "dns_sd_funct.h"
+namespace ZeroConf {
+extern int gQuickStop;
+namespace embeddedLib {
+#include "../dns_sd_funct.h"
static int gDaemonErr = kDNSServiceErr_NoError;
}}
-extern "C" {
#if defined(_WIN32)
#define _SSIZE_T
- #include <CommonServices.h>
- #include <DebugServices.h>
+ #include "CommonServices.h"
+ #include "DebugServices.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdarg.h>
- #include <stdio.h>
+ #include <stdio.h>
#define sockaddr_mdns sockaddr_in
#define AF_MDNS AF_INET
@@ -61,7 +62,7 @@ extern "C" {
// Disable warning: "nonstandard extension, function/data pointer conversion in expression"
#pragma warning(disable:4152)
- extern BOOL IsSystemServiceDisabled();
+ //extern BOOL IsSystemServiceDisabled();
#define sleep(X) Sleep((X) * 1000)
#define NOT_HAVE_SA_LEN
@@ -83,6 +84,7 @@ namespace ZeroConf { namespace embeddedLib {
if ( buffer ) { vsprintf( buffer, message, args ); OutputDebugStringA( buffer ); free( buffer ); }
WSASetLastError( err );
}
+}}
#else
#include <sys/fcntl.h> // For O_RDWR etc.
@@ -112,6 +114,7 @@ namespace ZeroConf { namespace embeddedLib {
#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
#endif
+extern "C" {
typedef struct
{
ipc_msg_hdr ipc_hdr;
@@ -217,7 +220,24 @@ static int read_all(dnssd_sock_t sd, char *buf, int len)
while (len)
{
- ssize_t num_read = recv(sd, buf, len, 0);
+ timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ fd_set readFds, writeFds, exceptFds;
+ memset(&readFds,0,sizeof(readFds));
+ memset(&writeFds,0,sizeof(writeFds));
+ memset(&exceptFds,0,sizeof(exceptFds));
+ FD_SET(sd, &readFds);
+ FD_SET(sd, &writeFds);
+ FD_SET(sd, &exceptFds);
+ ssize_t num_read = 0;
+ int nVal=select(sd+1, &readFds, &writeFds, &exceptFds, &timeout);
+ if (nVal < 1 || !(FD_ISSET(sd, &readFds) || FD_ISSET(sd, &exceptFds))) {
+ if (! ZeroConf::gQuickStop)
+ continue;
+ } else {
+ num_read = recv(sd, buf, len, 0);
+ }
// It is valid to get an interrupted system call error e.g., somebody attaching
// in a debugger, retry without failing
if ((num_read < 0) && (errno == EINTR)) { syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); continue; }
@@ -427,6 +447,7 @@ static void FreeDNSServiceOp(DNSServiceOp *x)
// Return a connected service ref (deallocate with DNSServiceRefDeallocate)
static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
{
+ static int quickCheck = 1;
#if APPLE_OSX_mDNSResponder
int NumTries = DNSSD_CLIENT_MAXTRIES;
#else
@@ -462,7 +483,7 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
}
// <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
- if (IsSystemServiceDisabled()) NumTries = DNSSD_CLIENT_MAXTRIES;
+ //if (IsSystemServiceDisabled()) NumTries = DNSSD_CLIENT_MAXTRIES;
#endif
sdr = static_cast<DNSServiceOp*>(malloc(sizeof(DNSServiceOp)));
@@ -543,11 +564,14 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
// daemon is still coming up. Rather than fail here, we'll wait a bit and try again.
// If, after four seconds, we still can't connect to the daemon,
// then we give up and return a failure code.
- if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again
- else { dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; }
+ if (++NumTries < DNSSD_CLIENT_MAXTRIES && !(quickCheck && NumTries > 1)) sleep(1); // Sleep a bit, then try again
+ else {
+ quickCheck = 0;
+ dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; }
}
//printf("ConnectToServer opened socket %d\n", sdr->sockfd);
}
+ quickCheck = 0;
*ref = sdr;
return kDNSServiceErr_NoError;
diff --git a/src/libs/zeroconf/embed/dnssd_ipc.h b/src/libs/zeroconf/embed/dnssd_ipc.h
index 833b51f050..e116515e35 100644
--- a/src/libs/zeroconf/embed/dnssd_ipc.h
+++ b/src/libs/zeroconf/embed/dnssd_ipc.h
@@ -29,7 +29,7 @@
#ifndef DNSSD_IPC_H
#define DNSSD_IPC_H
-#include "dns_sd_types.h"
+#include "../dns_sd_types.h"
//
// Common cross platform services
diff --git a/src/libs/zeroconf/embeddedLib.cpp b/src/libs/zeroconf/embeddedLib.cpp
index 8ce4be55df..38fc765f0f 100644
--- a/src/libs/zeroconf/embeddedLib.cpp
+++ b/src/libs/zeroconf/embeddedLib.cpp
@@ -44,6 +44,10 @@
#define EMBEDDED_LIB
#endif
+#ifdef Q_OS_WIN32
+#define EMBEDDED_LIB
+#endif
+
#ifdef EMBEDDED_LIB
#include "embed/dnssd_ipc.c"
#include "embed/dnssd_clientlib.c"
@@ -204,7 +208,7 @@ public:
return ProcessedOk;
}
- DNSServiceErrorType createConnection(ConnectionRef *sdRef)
+ DNSServiceErrorType createConnection(MainConnection *, ConnectionRef *sdRef)
{
return embeddedLib::DNSServiceCreateConnection(reinterpret_cast<DNSServiceRef*>(sdRef));
}
diff --git a/src/libs/zeroconf/servicebrowser.cpp b/src/libs/zeroconf/servicebrowser.cpp
index 9d3d9b07b0..0730857bde 100644
--- a/src/libs/zeroconf/servicebrowser.cpp
+++ b/src/libs/zeroconf/servicebrowser.cpp
@@ -135,20 +135,31 @@ public:
const QString &dnsSdDaemonPath);
private:
+ static const char *defaultmDnsSdLibName;
+ static const char *defaultmDNSDaemonName;
QMutex m_lock;
ZConfLib::Ptr m_defaultLib;
};
Q_GLOBAL_STATIC(ZeroConfLib, zeroConfLibInstance)
+#ifdef Q_OS_WIN
+ const char *ZeroConfLib::defaultmDnsSdLibName = "dnssd";
+ const char *ZeroConfLib::defaultmDNSDaemonName = "mdnssd.exe";
+#else
+ const char *ZeroConfLib::defaultmDnsSdLibName = "dns_sd";
+ const char *ZeroConfLib::defaultmDNSDaemonName = "mdnssd";
+#endif
+
ZeroConfLib::ZeroConfLib(): m_lock(QMutex::Recursive),
m_defaultLib(ZConfLib::createAvahiLib(QLatin1String("avahi-client"),
- ZConfLib::createDnsSdLib(QLatin1String("dns_sd"),
- ZConfLib::createEmbeddedLib(QLatin1String("mdnssd")))))
+ ZConfLib::createDnsSdLib(QLatin1String(defaultmDnsSdLibName),
+ ZConfLib::createEmbeddedLib(QLatin1String(defaultmDNSDaemonName)))))
{
- qRegisterMetaType<Service::ConstPtr>("ZeroConf::Service::ConstPtr");
- qRegisterMetaType<ErrorMessage::SeverityLevel>("ZeroConf::ErrorMessage::SeverityLevel");
- qRegisterMetaType<ErrorMessage>("ZeroConf::ErrorMessage");
+ qRegisterMetaType<ZeroConf::Service::ConstPtr>("ZeroConf::Service::ConstPtr");
+ qRegisterMetaType<ZeroConf::ErrorMessage::SeverityLevel>("ZeroConf::ErrorMessage::SeverityLevel");
+ qRegisterMetaType<ZeroConf::ErrorMessage>("ZeroConf::ErrorMessage");
+ qRegisterMetaType<QList<ZeroConf::ErrorMessage> >("QList<ZeroConf::ErrorMessage>");
}
ZConfLib::Ptr ZeroConfLib::defaultLib(){
@@ -186,6 +197,8 @@ void ZeroConfLib::setDefaultLib(LibUsage usage, const QString &avahiLibName,
namespace ZeroConf {
+int gQuickStop = 0;
+
// ----------------- ErrorMessage impl -----------------
/*!
\class ZeroConf::ErrorMessage
@@ -258,7 +271,24 @@ Service::~Service()
delete m_host;
}
-ZEROCONFSHARED_EXPORT QDebug operator<<(QDebug dbg, const Service &service)
+bool Service::operator==(const Service &o) const {
+ bool eq = m_fullName == o.m_fullName
+ && m_name == o.m_name && m_type == o.m_type
+ && m_domain == o.m_domain && m_port == o.m_port
+ && m_txtRecord == o.m_txtRecord && m_interfaceNr == o.m_interfaceNr
+ && m_outdated == m_outdated;
+ if (eq) {
+ if (m_host != o.m_host) {
+ if (m_host == 0 || o.m_host == 0)
+ return false;
+ return m_host->hostName() == o.m_host->hostName()
+ && m_host->addresses() == o.m_host->addresses();
+ }
+ }
+ return eq;
+}
+
+QDebug operator<<(QDebug dbg, const Service &service)
{
dbg.maybeSpace() << "Service{ name:" << service.name() << ", "
<< "type:" << service.type() << ", domain:" << service.domain() << ", "
@@ -292,6 +322,16 @@ ZEROCONFSHARED_EXPORT QDebug operator<<(QDebug dbg, const Service &service)
dbg << " interfaceNr:" << service.interfaceNr() << ", outdated:" << service.outdated() << " }";
return dbg.space();
}
+
+QDebug operator<<(QDebug dbg, const Service::ConstPtr &service){
+ if (service.data() == 0){
+ dbg << "Service{*NULL*}";
+ } else {
+ dbg << *service.data();
+ }
+ return dbg;
+}
+
// inline methods
/*!
\fn bool Service::outdated() const
@@ -368,16 +408,17 @@ void ServiceBrowser::startBrowsing(qint32 interfaceIndex)
/// create a new brower for the given service type
ServiceBrowser::ServiceBrowser(const QString &serviceType, const QString &domain,
AddressesSetting addressesSetting, QObject *parent)
- : QObject(parent),
+ : QObject(parent), timer(0),
d(new ServiceBrowserPrivate(serviceType, domain, addressesSetting == RequireAddresses,
MainConnectionPtr()))
{
+ connect(this,SIGNAL(activateAutoRefresh()),this,SLOT(autoRefresh()));
d->q = this;
}
ServiceBrowser::ServiceBrowser(const MainConnectionPtr &mainConnection, const QString &serviceType,
const QString &domain, AddressesSetting addressesSetting, QObject *parent)
- : QObject(parent),
+ : QObject(parent), timer(0),
d(new ServiceBrowserPrivate(serviceType, domain, addressesSetting == RequireAddresses,
mainConnection))
{
@@ -398,6 +439,11 @@ MainConnectionPtr ServiceBrowser::mainConnection() const
/// stops browsing, but does not delete all services found
void ServiceBrowser::stopBrowsing()
{
+ if (timer) {
+ timer->stop();
+ delete timer;
+ timer = 0;
+ }
d->stopBrowsing();
}
@@ -451,6 +497,16 @@ void ServiceBrowser::reconfirmService(Service::ConstPtr service)
d->reconfirmService(service);
}
+void ServiceBrowser::autoRefresh()
+{
+ QMutexLocker l(d->mainConnection->lock());
+ if (!timer) {
+ timer = new QTimer(this);
+ connect(timer,SIGNAL(timeout()),this,SLOT(triggerRefresh()));
+ timer->start(5000);
+ }
+}
+
// signals
/*!
\fn void ServiceBrowser::serviceChanged(
@@ -484,6 +540,24 @@ void ServiceBrowser::reconfirmService(Service::ConstPtr service)
services(), use this signal, not serviceChanged(), serviceAdded() or serviceRemoved() to know
about changes to the list.
*/
+/*!
+ \fn void errorMessage(ZeroConf::ErrorMessage::SeverityLevel severity, const QString &msg, ZeroConf::ServiceBrowser *browser)
+
+ This signal is called every time a warning or error is emitted (for example when a library
+ cannot be used and another one has to be used). Zeroconf will still work if severity < FailureLevel.
+*/
+/*!
+ \fn void hadFailure(const QList<ZeroConf::ErrorMessage> &messages, ZeroConf::ServiceBrowser *browser)
+
+ This signal is emitted only when a full failure has happened, and all the previous errors/attempts to set up zeroconf
+ are passed in messages.
+*/
+/*!
+ \fn void startedBrowsing(ZeroConf::ServiceBrowser *browser)
+
+ This signal is emitted when browsing has actually started.
+ One can rely on either startedBrowsing or hadFailure to be emitted.
+*/
// ----------------- library initialization impl -----------------
/*!
@@ -630,25 +704,32 @@ QString ServiceGatherer::fullName(){
return currentService->fullName();
}
-void ServiceGatherer::enactServiceChange()
+bool ServiceGatherer::enactServiceChange()
{
if (DEBUG_ZEROCONF)
qDebug() << "ServiceGatherer::enactServiceChange() for service "
<< currentService->fullName();
if (currentServiceCanBePublished()) {
+ if ((publishedService.data() == 0 && currentService == 0)
+ || (publishedService.data() != 0 && currentService != 0
+ && *publishedService == *currentService))
+ return false;
Service::Ptr nService = Service::Ptr(currentService);
- serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
if (publishedService) {
+ publishedService->invalidate();
serviceBrowser->nextActiveServices.removeOne(publishedService);
serviceBrowser->serviceRemoved(publishedService, serviceBrowser->q);
}
+ serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
publishedService = nService;
if (nService) {
serviceBrowser->nextActiveServices.append(nService);
serviceBrowser->serviceAdded(nService, serviceBrowser->q);
currentService = new Service(*currentService);
}
+ return true;
}
+ return false;
}
void ServiceGatherer::retireService()
@@ -659,8 +740,9 @@ void ServiceGatherer::retireService()
<< currentService->fullName();
Service::Ptr nService;
serviceBrowser->nextActiveServices.removeOne(publishedService);
- serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
+ publishedService->invalidate();
serviceBrowser->serviceRemoved(publishedService, serviceBrowser->q);
+ serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
publishedService = nService;
} else if (DEBUG_ZEROCONF){
qDebug() << "ServiceGatherer::retireService() for non published service "
@@ -672,13 +754,17 @@ void ServiceGatherer::stopResolve(ZK_IP_Protocol protocol)
{
if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv4)
&& (status & ResolveConnectionActive) != 0) {
- lib()->refDeallocate(resolveConnection);
+ QMutexLocker l(serviceBrowser->mainConnection->lock());
+ if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
+ lib()->refDeallocate(resolveConnection);
status &= ~ResolveConnectionActive;
serviceBrowser->updateFlowStatusForCancel();
}
if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv6)
&& (status & ResolveConnectionV6Active) != 0) {
- lib()->refDeallocate(resolveConnectionV6);
+ QMutexLocker l(serviceBrowser->mainConnection->lock());
+ if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
+ lib()->refDeallocate(resolveConnectionV6);
status &= ~ResolveConnectionV6Active;
serviceBrowser->updateFlowStatusForCancel();
}
@@ -757,7 +843,9 @@ void ServiceGatherer::restartResolve(ZK_IP_Protocol protocol)
void ServiceGatherer::stopTxt()
{
if ((status & TxtConnectionActive) == 0) return;
- lib()->refDeallocate(txtConnection);
+ QMutexLocker l(serviceBrowser->mainConnection->lock());
+ if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
+ lib()->refDeallocate(txtConnection);
status &= ~TxtConnectionActive;
serviceBrowser->updateFlowStatusForCancel();
}
@@ -783,7 +871,9 @@ void ServiceGatherer::restartTxt()
void ServiceGatherer::stopHostResolution()
{
if ((status & AddrConnectionActive) == 0) return;
- lib()->refDeallocate(addrConnection);
+ QMutexLocker l(serviceBrowser->mainConnection->lock());
+ if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
+ lib()->refDeallocate(addrConnection);
status &= ~AddrConnectionActive;
serviceBrowser->updateFlowStatusForCancel();
}
@@ -896,7 +986,6 @@ void ServiceGatherer::serviceResolveReply(DNSServiceFlags fl
}
return;
}
- if (publishedService) publishedService->invalidate(); // delay this to enactServiceChange?
serviceBrowser->updateFlowStatusForFlags(flags);
uint16_t nKeys = txtRecordGetCount(txtLen, rawTxtRecord);
for (uint16_t i = 0; i < nKeys; ++i){
@@ -1040,7 +1129,7 @@ void ServiceGatherer::addrReply(DNSServiceFlags flags,
const struct sockaddr *address,
uint32_t /*ttl*/) // should we use this???
{
- if (errorCode != kDNSServiceErr_NoError){
+ if (errorCode != kDNSServiceErr_NoError) {
if (errorCode == kDNSServiceErr_Timeout){
if ((status & AddrConnectionSuccess) == 0){
qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
@@ -1087,8 +1176,12 @@ void ServiceGatherer::addrReply(DNSServiceFlags flags,
} else {
if (!addrNow.contains(newAddr)){
switch (newAddr.protocol()){
- case QAbstractSocket::IPv6Protocol:
- addrNow.insert(0, newAddr); // prefers IPv6 addresses
+#ifdef Q_OS_WIN
+ case QAbstractSocket::IPv4Protocol: // prefers IPv4 addresses
+#else
+ case QAbstractSocket::IPv6Protocol: // prefers IPv6 addresses
+#endif
+ addrNow.insert(0, newAddr);
break;
default:
addrNow.append(newAddr);
@@ -1238,6 +1331,7 @@ void ServiceBrowserPrivate::maybeUpdateLists()
qint64 now = QDateTime::currentMSecsSinceEpoch();
QList<QString>::iterator i = knownServices.begin(), endi = knownServices.end();
QMap<QString, ServiceGatherer::Ptr>::iterator j = gatherers.begin();
+ bool hasServicesChanges = false;
while (i != endi && j != gatherers.end()) {
const QString vi = *i;
QString vj = j.value()->fullName();
@@ -1252,6 +1346,7 @@ void ServiceBrowserPrivate::maybeUpdateLists()
pendingGatherers.removeAll(j.value());
j.value()->retireService();
j = gatherers.erase(j);
+ hasServicesChanges = true;
} else {
++j;
}
@@ -1265,20 +1360,26 @@ void ServiceBrowserPrivate::maybeUpdateLists()
pendingGatherers.removeAll(j.value());
j.value()->retireService();
j = gatherers.erase(j);
+ hasServicesChanges = true;
} else {
++j;
}
}
foreach (const ServiceGatherer::Ptr &g, pendingGatherers)
- g->enactServiceChange();
- {
- QMutexLocker l(mainConnection->lock());
- activeServices = nextActiveServices;
+ hasServicesChanges = hasServicesChanges || g->enactServiceChange();
+ if (hasServicesChanges) {
+ {
+ QMutexLocker l(mainConnection->lock());
+ activeServices = nextActiveServices;
+ }
+ emit q->servicesUpdated(q);
}
- emit q->servicesUpdated(q);
}
- if (shouldRefresh)
- refresh();
+ {
+ QMutexLocker l(mainConnection->lock());
+ if (shouldRefresh)
+ refresh();
+ }
}
/// callback announcing
@@ -1332,6 +1433,11 @@ void ServiceBrowserPrivate::browseReply(DNSServiceFlags flag
maybeUpdateLists(); // avoid?
}
+void ServiceBrowserPrivate::activateAutoRefresh()
+{
+ emit q->activateAutoRefresh();
+}
+
void ServiceBrowserPrivate::startBrowsing(quint32 interfaceIndex)
{
this->interfaceIndex = interfaceIndex;
@@ -1363,11 +1469,18 @@ bool ServiceBrowserPrivate::internalStartBrowsing()
void ServiceBrowserPrivate::triggerRefresh()
{
- QMutexLocker l(mainConnection->lock());
- const qint64 msecDelay = 5100;
- delayDeletesUntil = QDateTime::currentMSecsSinceEpoch() + msecDelay;
- stopBrowsing();
- shouldRefresh = true;
+ {
+ QMutexLocker l(mainConnection->lock());
+ const qint64 msecDelay = 5100;
+ delayDeletesUntil = QDateTime::currentMSecsSinceEpoch() + msecDelay;
+ stopBrowsing();
+ shouldRefresh = true;
+ }
+ {
+ QMutexLocker l(mainConnection->mainThreadLock());
+ if (!browsing)
+ refresh();
+ }
}
void ServiceBrowserPrivate::refresh()
@@ -1443,18 +1556,30 @@ void ServiceBrowserPrivate::hadFailure(const QList<ErrorMessage> &messages)
emit q->hadFailure(messages, q);
}
+void ServiceBrowserPrivate::startedBrowsing()
+{
+ emit q->startedBrowsing(q);
+}
+
// ----------------- MainConnection impl -----------------
void MainConnection::stop(bool wait)
{
-#if QT_VERSION >= 0x050000
- if (m_status.load() < Stopping)
-#else
- if (m_status < Stopping)
-#endif
+ {
+ if (QThread::currentThread() == m_thread)
+ qCritical() << "ERROR ZerocConf::MainConnection::stop called from m_thread";
+ // This will most likely lock if called from the connection thread (as mainThreadLock is non recusive)
+ // Making it recursive would open a hole during the startup of the thread.
+ // As of now this should always be called during the destruction of a browser,
+ // so from another thread.
increaseStatusTo(Stopping);
- if (m_mainRef)
+ QMutexLocker l(mainThreadLock());
+ QMutexLocker l2(lock());
+ }
+ if (m_mainRef) {
lib->stopConnection(m_mainRef);
+ m_mainRef = 0;
+ }
if (!m_thread)
increaseStatusTo(Stopped);
else if (wait && QThread::currentThread() != m_thread)
@@ -1462,39 +1587,34 @@ void MainConnection::stop(bool wait)
}
MainConnection::MainConnection():
- flowStatus(NormalRFS), lib(zeroConfLibInstance()->defaultLib()), m_lock(QMutex::Recursive), m_mainRef(0),
- m_failed(false), m_status(Starting), m_nErrs(0)
+ flowStatus(NormalRFS),
+ lib(zeroConfLibInstance()->defaultLib()), m_lock(QMutex::Recursive),
+ m_mainThreadLock(QMutex::NonRecursive), m_mainRef(0), m_failed(false), m_status(Starting), m_nErrs(0)
{
if (lib.isNull()){
qDebug() << "could not load a valid library for ZeroConf::MainConnection, failing";
} else {
m_thread = new ConnectionThread(*this);
+ m_mainThreadLock.lock();
m_thread->start(); // delay startup??
}
}
MainConnection::~MainConnection()
{
- stop();
+ gQuickStop = 1;
+ stop(true);
+ gQuickStop = 0;
delete m_thread;
- // to do
}
bool MainConnection::increaseStatusTo(int s)
{
-#if QT_VERSION >= 0x050000
- int sAtt = m_status.load();
-#else
- int sAtt = m_status;
-#endif
+ int sAtt = status();
while (sAtt < s){
if (m_status.testAndSetRelaxed(sAtt, s))
return true;
-#if QT_VERSION >= 0x050000
- sAtt = m_status.load();
-#else
- sAtt = m_status;
-#endif
+ sAtt = status();
}
return false;
}
@@ -1504,17 +1624,18 @@ QMutex *MainConnection::lock()
return &m_lock;
}
+QMutex *MainConnection::mainThreadLock()
+{
+ return &m_mainThreadLock;
+}
+
void MainConnection::waitStartup()
{
int sAtt;
while (true){
{
QMutexLocker l(lock());
-#if QT_VERSION >= 0x050000
- sAtt = m_status.load();
-#else
- sAtt = m_status;
-#endif
+ sAtt = status();
if (sAtt >= Running)
return;
}
@@ -1528,17 +1649,12 @@ void MainConnection::addBrowser(ServiceBrowserPrivate *browser)
QList<ErrorMessage> errs;
{
QMutexLocker l(lock());
-#if QT_VERSION >= 0x050000
- actualStatus = m_status.load();
-#else
- actualStatus = m_status;
-#endif
+ actualStatus = status();
m_browsers.append(browser);
errs = m_errors;
}
- if (actualStatus == Running) {
- browser->internalStartBrowsing();
- }
+ if (actualStatus == Running && browser->internalStartBrowsing())
+ browser->startedBrowsing();
bool didFail = false;
foreach (const ErrorMessage &msg, errs) {
browser->errorMessage(msg.severity, msg.msg);
@@ -1606,11 +1722,7 @@ void MainConnection::abortLib(){
void MainConnection::createConnection()
{
gotoValidLib();
-#if QT_VERSION >= 0x050000
- while (m_status.load() <= Running) {
-#else
- while (m_status <= Running) {
-#endif
+ while (status() <= Running) {
if (!lib) {
increaseStatusTo(Stopped);
break;
@@ -1619,7 +1731,7 @@ void MainConnection::createConnection()
uint32_t size = (uint32_t)sizeof(uint32_t);
DNSServiceErrorType err = lib->getProperty(kDNSServiceProperty_DaemonVersion, &version, &size);
if (err == kDNSServiceErr_NoError){
- DNSServiceErrorType error = lib->createConnection(&m_mainRef);
+ DNSServiceErrorType error = lib->createConnection(this, &m_mainRef);
if (error != kDNSServiceErr_NoError){
appendError(ErrorMessage::WarningLevel, tr("MainConnection using lib %1 failed the initialization of mainRef with error %2")
.arg(lib->name()).arg(error));
@@ -1635,8 +1747,9 @@ void MainConnection::createConnection()
}
for (int i = waitingBrowsers.count(); i-- != 0; ){
ServiceBrowserPrivate *actualBrowser = waitingBrowsers[i];
- if (actualBrowser && !actualBrowser->browsing)
- actualBrowser->internalStartBrowsing();
+ if (actualBrowser && !actualBrowser->browsing
+ && actualBrowser->internalStartBrowsing())
+ actualBrowser->startedBrowsing();
}
break;
}
@@ -1671,11 +1784,11 @@ ZConfLib::RunLoopStatus MainConnection::handleEvent()
nextEvent = bAtt->delayDeletesUntil;
}
if (nextEvent <= now)
- nextEvent = 5000;
+ nextEvent = -1;
else
nextEvent -= now;
maybeUpdateLists();
- ZConfLib::RunLoopStatus err = lib->processOneEvent(m_mainRef, nextEvent);
+ ZConfLib::RunLoopStatus err = lib->processOneEvent(this, m_mainRef, nextEvent);
if (err != ZConfLib::ProcessedOk && err != ZConfLib::ProcessedIdle) {
qDebug() << "processOneEvent returned " << err;
++m_nErrs;
@@ -1701,19 +1814,22 @@ void MainConnection::destroyConnection()
void MainConnection::handleEvents()
{
- if (!m_status.testAndSetAcquire(Starting, Started)){
- appendError(ErrorMessage::WarningLevel, tr("MainConnection::handleEvents called with m_status != Starting, aborting"));
- increaseStatusTo(Stopped);
- return;
+ try {
+ if (!m_status.testAndSetAcquire(Starting, Started)){
+ appendError(ErrorMessage::WarningLevel, tr("MainConnection::handleEvents called with m_status != Starting, aborting"));
+ increaseStatusTo(Stopped);
+ return;
+ }
+ m_nErrs = 0;
+ createConnection();
+ } catch(...) {
+ mainThreadLock()->unlock();
+ throw;
}
- m_nErrs = 0;
- createConnection();
+ mainThreadLock()->unlock();
increaseStatusTo(Running);
-#if QT_VERSION >= 0x050000
- while (m_status.load() < Stopping) {
-#else
- while (m_status < Stopping) {
-#endif
+ while (status() < Stopping) {
+ QMutexLocker l(mainThreadLock());
if (m_nErrs > 10)
increaseStatusTo(Stopping);
switch (handleEvent()) {
@@ -1740,24 +1856,31 @@ void MainConnection::handleEvents()
if (m_nErrs > 0){
QString browsersNames = (m_browsers.isEmpty() ? QString() : m_browsers.at(0)->serviceType)
+ ((m_browsers.count() > 1) ? QString::fromLatin1(",...") : QString());
- appendError(ErrorMessage::FailureLevel, tr("MainConnection for [%1] accumulated %2 consecutive errors, aborting")
- .arg(browsersNames).arg(m_nErrs));
+ if (isOk())
+ appendError(ErrorMessage::FailureLevel, tr("MainConnection for [%1] accumulated %2 consecutive errors, aborting")
+ .arg(browsersNames).arg(m_nErrs));
}
increaseStatusTo(Stopped);
}
ZConfLib::ConnectionRef MainConnection::mainRef()
{
-#if QT_VERSION >= 0x050000
- while (m_status.load() < Running){
-#else
- while (m_status < Running){
-#endif
+ while (status() < Running){
QThread::yieldCurrentThread();
}
return m_mainRef;
}
+MainConnection::Status MainConnection::status()
+{
+#if QT_VERSION >= 0x050000
+ return static_cast<MainConnection::Status>(m_status.load());
+#else
+ int val = m_status;
+ return static_cast<MainConnection::Status>(val);
+#endif
+}
+
QList<ErrorMessage> MainConnection::errors()
{
QMutexLocker l(lock());
@@ -1820,45 +1943,50 @@ void ZConfLib::setError(bool failure, const QString &eMsg)
m_isOk = !failure;
}
-ZConfLib::RunLoopStatus ZConfLib::processOneEvent(ConnectionRef cRef, qint64 maxMsBlock)
+ZConfLib::RunLoopStatus ZConfLib::processOneEvent(MainConnection *mainConnection,
+ ConnectionRef cRef, qint64 maxMsBlock)
{
if (maxMsBlock < 0) { // just block
- return processOneEventBlock(cRef);
+ maxMsBlock = MAX_SEC_FOR_READ * static_cast<qint64>(1000);
+ }
+ // some stuff could be extracted for maximal performance
+ int dns_sd_fd = (cRef ? refSockFD(cRef) : -1);
+ int nfds = dns_sd_fd + 1;
+ fd_set readfds;
+ struct timeval tv;
+ int result;
+ dns_sd_fd = (cRef ? refSockFD(cRef) : -1);
+ if (dns_sd_fd < 0)
+ return ProcessedError;
+ nfds = dns_sd_fd + 1;
+ FD_ZERO(&readfds);
+ FD_SET(dns_sd_fd, &readfds);
+
+ if (maxMsBlock > MAX_SEC_FOR_READ * static_cast<qint64>(1000)) {
+ tv.tv_sec = MAX_SEC_FOR_READ;
+ tv.tv_usec = 0;
} else {
- // some stuff could be extracted for maximal performance
- int dns_sd_fd = (cRef ? refSockFD(cRef) : -1);
- int nfds = dns_sd_fd + 1;
- fd_set readfds;
- struct timeval tv;
- int result;
- dns_sd_fd = (cRef ? refSockFD(cRef) : -1);
- if (dns_sd_fd < 0)
- return ProcessedError;
- nfds = dns_sd_fd + 1;
- FD_ZERO(&readfds);
- FD_SET(dns_sd_fd, &readfds);
-
- if (maxMsBlock > MAX_SEC_FOR_READ * static_cast<qint64>(1000)) {
- tv.tv_sec = MAX_SEC_FOR_READ;
- tv.tv_usec = 0;
- } else {
- tv.tv_sec = static_cast<time_t>(maxMsBlock / 1000);
- tv.tv_usec = static_cast<suseconds_t>((maxMsBlock % 1000) * 1000);
- }
- result = select(nfds, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
- if (result > 0) {
- if (FD_ISSET(dns_sd_fd, &readfds))
- return processOneEventBlock(cRef);
- } else if (result == 0) {
- return ProcessedIdle;
- } else if (errno != EINTR) {
- if (DEBUG_ZEROCONF)
- qDebug() << "select() returned " << result << " errno " << errno
- << strerror(errno);
- return ProcessedError;
- }
- return ProcessedIdle; // change? should never happen anyway
+ tv.tv_sec = static_cast<time_t>(maxMsBlock / 1000);
+ tv.tv_usec = static_cast<suseconds_t>((maxMsBlock % 1000) * 1000);
+ }
+ QMutex *lock = (mainConnection ? mainConnection->mainThreadLock() : 0);
+ if (lock)
+ lock->unlock();
+ result = select(nfds, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
+ if (lock)
+ lock->lock();
+ if (result > 0) {
+ if (FD_ISSET(dns_sd_fd, &readfds))
+ return processOneEventBlock(cRef);
+ } else if (result == 0) {
+ return ProcessedIdle;
+ } else if (errno != EINTR) {
+ if (DEBUG_ZEROCONF)
+ qDebug() << "select() returned " << result << " errno " << errno
+ << strerror(errno);
+ return ProcessedError;
}
+ return ProcessedIdle; // change? should never happen anyway
}
} // namespace Internal
diff --git a/src/libs/zeroconf/servicebrowser.h b/src/libs/zeroconf/servicebrowser.h
index 88f01ab4b2..a3dfd338f9 100644
--- a/src/libs/zeroconf/servicebrowser.h
+++ b/src/libs/zeroconf/servicebrowser.h
@@ -39,6 +39,7 @@
#include <QObject>
#include <QSharedPointer>
#include <QStringList>
+#include <QTimer>
QT_FORWARD_DECLARE_CLASS(QHostInfo)
@@ -99,6 +100,7 @@ public:
const ServiceTxtRecord &txtRecord() const { return m_txtRecord; }
const QHostInfo *host() const { return m_host; }
int interfaceNr() const { return m_interfaceNr; }
+ bool operator==(const Service &o) const;
bool invalidate() { bool res = m_outdated; m_outdated = true; return res; }
private:
@@ -114,6 +116,7 @@ private:
};
ZEROCONFSHARED_EXPORT QDebug operator<<(QDebug dbg, const Service &service);
+ZEROCONFSHARED_EXPORT QDebug operator<<(QDebug dbg, const Service::ConstPtr &service);
class ZEROCONFSHARED_EXPORT ServiceBrowser : public QObject
{
@@ -133,7 +136,6 @@ public:
void startBrowsing(qint32 interfaceIndex = 0);
void stopBrowsing();
- void triggerRefresh();
bool isBrowsing() const;
bool didFail() const;
@@ -145,7 +147,11 @@ public:
QList<Service::ConstPtr> services() const;
void reconfirmService(Service::ConstPtr service);
+public slots:
+ void triggerRefresh();
+ void autoRefresh();
signals:
+ void activateAutoRefresh();
void serviceChanged(const ZeroConf::Service::ConstPtr &oldService,
const ZeroConf::Service::ConstPtr &newService, ZeroConf::ServiceBrowser *browser);
void serviceAdded(const ZeroConf::Service::ConstPtr &service,
@@ -155,7 +161,9 @@ signals:
void servicesUpdated(ZeroConf::ServiceBrowser *browser);
void errorMessage(ZeroConf::ErrorMessage::SeverityLevel severity, const QString &msg, ZeroConf::ServiceBrowser *browser);
void hadFailure(const QList<ZeroConf::ErrorMessage> &messages, ZeroConf::ServiceBrowser *browser);
+ void startedBrowsing(ZeroConf::ServiceBrowser *browser);
private:
+ QTimer *timer;
Internal::ServiceBrowserPrivate *d;
};
diff --git a/src/libs/zeroconf/servicebrowser_p.h b/src/libs/zeroconf/servicebrowser_p.h
index 8cd880d18a..37239c6819 100644
--- a/src/libs/zeroconf/servicebrowser_p.h
+++ b/src/libs/zeroconf/servicebrowser_p.h
@@ -49,6 +49,9 @@
class QHostInfo;
namespace ZeroConf {
+
+class ServiceBrowser;
+
namespace Internal {
class ServiceGatherer;
@@ -109,8 +112,10 @@ public:
ServiceBrowserPrivate *browser) = 0;
virtual DNSServiceErrorType getProperty(const char *property, void *result, uint32_t *size) = 0;
virtual RunLoopStatus processOneEventBlock(ConnectionRef sdRef) = 0;
- virtual RunLoopStatus processOneEvent(ConnectionRef sdRef, qint64 maxMsBlock);
- virtual DNSServiceErrorType createConnection(ConnectionRef *sdRef) = 0;
+ virtual RunLoopStatus processOneEvent(MainConnection *mainConnection,
+ ConnectionRef sdRef, qint64 maxMsBlock);
+ virtual DNSServiceErrorType createConnection(MainConnection *mainConnection,
+ ConnectionRef *sdRef) = 0;
virtual void stopConnection(ConnectionRef cRef) = 0;
virtual void destroyConnection(ConnectionRef *sdRef) = 0;
virtual int refSockFD(ConnectionRef sdRef) = 0;
@@ -154,7 +159,7 @@ public:
};
QString fullName();
- void enactServiceChange();
+ bool enactServiceChange();
void retireService();
Ptr gatherer();
@@ -232,6 +237,7 @@ public:
MainConnection();
~MainConnection();
QMutex *lock();
+ QMutex *mainThreadLock();
void waitStartup();
void stop(bool wait = true);
void addBrowser(ServiceBrowserPrivate *browser);
@@ -247,13 +253,14 @@ public:
bool increaseStatusTo(int s);
void handleEvents();
ZConfLib::ConnectionRef mainRef();
+ Status status();
QList<ErrorMessage> errors();
bool isOk();
private:
void appendError(ErrorMessage::SeverityLevel severity, const QString &msg);
- mutable QMutex m_lock;
+ mutable QMutex m_lock, m_mainThreadLock;
QList<ServiceBrowserPrivate *> m_browsers;
ZConfLib::ConnectionRef m_mainRef;
bool m_failed;
@@ -264,7 +271,6 @@ private:
};
class ServiceBrowserPrivate {
- friend class ServiceBrowser;
public:
ServiceBrowser *q;
QString serviceType;
@@ -309,6 +315,7 @@ public:
uint32_t interfaceIndex, ZK_IP_Protocol proto, DNSServiceErrorType errorCode,
const char *serviceName, const char *regtype, const char *replyDomain);
+ void activateAutoRefresh();
void serviceChanged(const Service::ConstPtr &oldService, const Service::ConstPtr &newService,
ServiceBrowser *browser);
void serviceAdded(const Service::ConstPtr &service, ServiceBrowser *browser);
@@ -316,6 +323,7 @@ public:
void servicesUpdated(ServiceBrowser *browser);
void errorMessage(ErrorMessage::SeverityLevel severity, const QString &msg);
void hadFailure(const QList<ErrorMessage> &msgs);
+ void startedBrowsing();
};
class ConnectionThread: public QThread {
diff --git a/src/libs/zeroconf/zeroconf.qbs b/src/libs/zeroconf/zeroconf.qbs
index 17bf4d2cea..279ce89510 100644
--- a/src/libs/zeroconf/zeroconf.qbs
+++ b/src/libs/zeroconf/zeroconf.qbs
@@ -16,7 +16,8 @@ QtcLibrary {
Properties {
condition: qbs.targetOS == "linux"
cpp.defines: outer.concat([
- "_GNU_SOURCE HAVE_IPV6",
+ "_GNU_SOURCE",
+ "HAVE_IPV6",
"USES_NETLINK",
"HAVE_LINUX",
"TARGET_OS_LINUX"
diff --git a/src/pluginjsonmetadata.xsl b/src/pluginjsonmetadata.xsl
new file mode 100644
index 0000000000..dff00332b7
--- /dev/null
+++ b/src/pluginjsonmetadata.xsl
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- XSL sheet to transform Qt Creator's pluginspec files into json files required
+ for the new Qt 5 plugin system. -->
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:strip-space elements="plugin vendor category description url"/>
+<xsl:template match="/">
+{
+ <xsl:apply-templates/>
+}
+</xsl:template>
+<xsl:template match="license"/>
+<xsl:template match="dependencyList"/>
+<xsl:template match="copyright"/>
+<xsl:template match="argumentList"/>
+<xsl:template match="vendor">
+"Vendor" : "<xsl:apply-templates/>",
+</xsl:template>
+<xsl:template match="category">
+"Category" : "<xsl:apply-templates/>",
+</xsl:template>
+<xsl:template match="description">
+"Description" : "<xsl:apply-templates/>",
+</xsl:template>
+<xsl:template match="url">
+"Url" : "<xsl:apply-templates/>"
+</xsl:template>
+</xsl:stylesheet>
diff --git a/src/plugins/QtcPlugin.qbs b/src/plugins/QtcPlugin.qbs
index da48870f3b..8d4ca84bb1 100644
--- a/src/plugins/QtcPlugin.qbs
+++ b/src/plugins/QtcPlugin.qbs
@@ -20,7 +20,7 @@ Product {
Depends { name: "pluginspec" }
Depends { name: 'cpp' }
- cpp.defines: [ name.toUpperCase() + "_LIBRARY" ]
+ cpp.defines: [name.toUpperCase() + "_LIBRARY"]
cpp.rpaths: ["$ORIGIN/../../.."]
Group {
diff --git a/src/plugins/analyzerbase/AnalyzerBase.pluginspec.in b/src/plugins/analyzerbase/AnalyzerBase.pluginspec.in
index 3bcb94e25c..70beca98a1 100644
--- a/src/plugins/analyzerbase/AnalyzerBase.pluginspec.in
+++ b/src/plugins/analyzerbase/AnalyzerBase.pluginspec.in
@@ -17,5 +17,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"RemoteLinux\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
</dependencyList>
</plugin>
diff --git a/src/plugins/analyzerbase/analyzerbase.qbs b/src/plugins/analyzerbase/analyzerbase.qbs
index 9ca081487e..7b8f2ff30f 100644
--- a/src/plugins/analyzerbase/analyzerbase.qbs
+++ b/src/plugins/analyzerbase/analyzerbase.qbs
@@ -7,7 +7,7 @@ QtcPlugin {
Depends { name: "qt"; submodules: ['gui'] }
Depends { name: "Core" }
- Depends { name: "CPlusPlus" }
+ Depends { name: "CppTools" }
Depends { name: "RemoteLinux" }
Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" }
diff --git a/src/plugins/analyzerbase/analyzerbase_dependencies.pri b/src/plugins/analyzerbase/analyzerbase_dependencies.pri
index 65e1ef20fa..8ac7abd387 100644
--- a/src/plugins/analyzerbase/analyzerbase_dependencies.pri
+++ b/src/plugins/analyzerbase/analyzerbase_dependencies.pri
@@ -2,3 +2,4 @@ include(../../plugins/coreplugin/coreplugin.pri)
include(../../libs/cplusplus/cplusplus.pri)
include(../../plugins/projectexplorer/projectexplorer.pri)
include(../../plugins/remotelinux/remotelinux.pri)
+include(../../plugins/cpptools/cpptools.pri)
diff --git a/src/plugins/analyzerbase/analyzerplugin.cpp b/src/plugins/analyzerbase/analyzerplugin.cpp
index a8a3367c3e..fd7084de9c 100644
--- a/src/plugins/analyzerbase/analyzerplugin.cpp
+++ b/src/plugins/analyzerbase/analyzerplugin.cpp
@@ -43,7 +43,7 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/task.h>
#include <projectexplorer/taskhub.h>
@@ -83,10 +83,9 @@ bool AnalyzerPlugin::initialize(const QStringList &arguments, QString *errorStri
addAutoReleasedObject(new AnalyzerRunControlFactory());
// Task integration.
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
- ProjectExplorer::TaskHub *hub = pm->getObject<ProjectExplorer::TaskHub>();
//: Category under which Analyzer tasks are listed in Issues view
- hub->addCategory(Core::Id(Constants::ANALYZERTASK_ID), tr("Analyzer"));
+ ProjectExplorer::ProjectExplorerPlugin::instance()->taskHub()
+ ->addCategory(Core::Id(Constants::ANALYZERTASK_ID), tr("Analyzer"));
return true;
}
diff --git a/src/plugins/analyzerbase/analyzerplugin.h b/src/plugins/analyzerbase/analyzerplugin.h
index 7db7c8c206..da5d80588c 100644
--- a/src/plugins/analyzerbase/analyzerplugin.h
+++ b/src/plugins/analyzerbase/analyzerplugin.h
@@ -43,6 +43,7 @@ namespace Internal {
class AnalyzerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AnalyzerBase.json")
public:
static AnalyzerPlugin *instance();
diff --git a/src/plugins/analyzerbase/analyzerruncontrol.cpp b/src/plugins/analyzerbase/analyzerruncontrol.cpp
index bda27e8fbf..5cb91674a5 100644
--- a/src/plugins/analyzerbase/analyzerruncontrol.cpp
+++ b/src/plugins/analyzerbase/analyzerruncontrol.cpp
@@ -39,8 +39,8 @@
#include "analyzermanager.h"
#include "analyzerstartparameters.h"
-#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/applicationrunconfiguration.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <projectexplorer/taskhub.h>
@@ -115,10 +115,9 @@ void AnalyzerRunControl::start()
AnalyzerManager::handleToolStarted();
- // clear about-to-be-outdated tasks
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
- TaskHub *hub = pm->getObject<TaskHub>();
- hub->clearTasks(Core::Id(Constants::ANALYZERTASK_ID));
+ // Clear about-to-be-outdated tasks.
+ ProjectExplorerPlugin::instance()->taskHub()
+ ->clearTasks(Core::Id(Constants::ANALYZERTASK_ID));
if (d->m_engine->start()) {
d->m_isRunning = true;
@@ -177,8 +176,7 @@ void AnalyzerRunControl::receiveOutput(const QString &text, Utils::OutputFormat
void AnalyzerRunControl::addTask(Task::TaskType type, const QString &description,
const QString &file, int line)
{
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
- TaskHub *hub = pm->getObject<TaskHub>();
+ TaskHub *hub = ProjectExplorerPlugin::instance()->taskHub();
hub->addTask(Task(type, description, Utils::FileName::fromUserInput(file), line,
Core::Id(Constants::ANALYZERTASK_ID)));
hub->popup(false);
diff --git a/src/plugins/analyzerbase/analyzerstartparameters.h b/src/plugins/analyzerbase/analyzerstartparameters.h
index 651e05d7ce..d84bd95771 100644
--- a/src/plugins/analyzerbase/analyzerstartparameters.h
+++ b/src/plugins/analyzerbase/analyzerstartparameters.h
@@ -51,7 +51,6 @@ class ANALYZER_EXPORT AnalyzerStartParameters
{
public:
AnalyzerStartParameters()
- : connParams(Utils::SshConnectionParameters::NoProxy)
{}
StartMode startMode;
diff --git a/src/plugins/analyzerbase/analyzerutils.cpp b/src/plugins/analyzerbase/analyzerutils.cpp
index 902bac3404..870cb160df 100644
--- a/src/plugins/analyzerbase/analyzerutils.cpp
+++ b/src/plugins/analyzerbase/analyzerutils.cpp
@@ -41,7 +41,7 @@
#include <utils/qtcassert.h>
#include <cplusplus/ExpressionUnderCursor.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/LookupItem.h>
#include <cplusplus/TypeOfExpression.h>
diff --git a/src/plugins/analyzerbase/startremotedialog.cpp b/src/plugins/analyzerbase/startremotedialog.cpp
index 4e071aa47d..25b784f607 100644
--- a/src/plugins/analyzerbase/startremotedialog.cpp
+++ b/src/plugins/analyzerbase/startremotedialog.cpp
@@ -115,7 +115,7 @@ void StartRemoteDialog::validate()
Utils::SshConnectionParameters StartRemoteDialog::sshParams() const
{
- Utils::SshConnectionParameters params(Utils::SshConnectionParameters::NoProxy);
+ Utils::SshConnectionParameters params;
params.host = m_ui->host->text();
params.userName = m_ui->user->text();
if (m_ui->keyFile->isValid()) {
diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
index e6d1b93b57..4c3c4e6607 100644
--- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp
+++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
@@ -84,7 +84,7 @@ bool AutogenStepFactory::canCreate(BuildStepList *parent, const QString &id) con
if (parent->target()->project()->id() != QLatin1String(Constants::AUTOTOOLS_PROJECT_ID))
return false;
- if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_BUILD)
+ if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_BUILD))
return false;
return QLatin1String(AUTOGEN_STEP_ID) == id;
@@ -282,7 +282,7 @@ void AutogenStepConfigWidget::updateDetails()
param.setMacroExpander(bc->macroExpander());
param.setEnvironment(bc->environment());
param.setWorkingDirectory(bc->buildDirectory());
- param.setCommand("autogen.sh");
+ param.setCommand(QLatin1String("autogen.sh"));
param.setArguments(m_autogenStep->additionalArguments());
m_summaryText = param.summary(displayName());
emit updateSummary();
diff --git a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
index 09d7d3cc2a..587bd68333 100644
--- a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
+++ b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
@@ -83,7 +83,7 @@ bool AutoreconfStepFactory::canCreate(BuildStepList *parent, const QString &id)
if (parent->target()->project()->id() != QLatin1String(Constants::AUTOTOOLS_PROJECT_ID))
return false;
- if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_BUILD)
+ if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_BUILD))
return false;
return QLatin1String(AUTORECONF_STEP_ID) == id;
@@ -166,7 +166,7 @@ bool AutoreconfStep::init()
pp->setMacroExpander(bc->macroExpander());
pp->setEnvironment(bc->environment());
pp->setWorkingDirectory(bc->buildDirectory());
- pp->setCommand("autoreconf");
+ pp->setCommand(QLatin1String("autoreconf"));
pp->setArguments(additionalArguments());
return AbstractProcessStep::init();
@@ -276,7 +276,7 @@ void AutoreconfStepConfigWidget::updateDetails()
param.setMacroExpander(bc->macroExpander());
param.setEnvironment(bc->environment());
param.setWorkingDirectory(bc->buildDirectory());
- param.setCommand("autoreconf");
+ param.setCommand(QLatin1String("autoreconf"));
param.setArguments(m_autoreconfStep->additionalArguments());
m_summaryText = param.summary(displayName());
emit updateSummary();
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsbuildconfiguration.cpp b/src/plugins/autotoolsprojectmanager/autotoolsbuildconfiguration.cpp
index b0986cfc73..92eafe762b 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsbuildconfiguration.cpp
+++ b/src/plugins/autotoolsprojectmanager/autotoolsbuildconfiguration.cpp
@@ -170,7 +170,7 @@ AutotoolsBuildConfiguration *AutotoolsBuildConfigurationFactory::create(Target *
bc->setDisplayName(buildConfigurationName);
t->addBuildConfiguration(bc);
- t->addDeployConfiguration(t->createDeployConfiguration(DEFAULT_DEPLOYCONFIGURATION_ID));
+ t->addDeployConfiguration(t->createDeployConfiguration(QLatin1String(DEFAULT_DEPLOYCONFIGURATION_ID)));
// User needs to choose where the executable file is.
// TODO: Parse the file in *Anjuta style* to be able to add custom RunConfigurations.
t->addRunConfiguration(new CustomExecutableRunConfiguration(t));
@@ -181,7 +181,7 @@ AutotoolsBuildConfiguration *AutotoolsBuildConfigurationFactory::create(Target *
AutotoolsBuildConfiguration *AutotoolsBuildConfigurationFactory::createDefaultConfiguration(AutotoolsTarget *target) const
{
AutotoolsBuildConfiguration *bc = new AutotoolsBuildConfiguration(target);
- BuildStepList *buildSteps = bc->stepList(BUILDSTEPS_BUILD);
+ BuildStepList *buildSteps = bc->stepList(QLatin1String(BUILDSTEPS_BUILD));
// ### Build Steps Build ###
// autogen.sh or autoreconf
@@ -205,7 +205,7 @@ AutotoolsBuildConfiguration *AutotoolsBuildConfigurationFactory::createDefaultCo
makeStep->setBuildTarget(QLatin1String("all"), /*on =*/ true);
// ### Build Steps Clean ###
- BuildStepList *cleanSteps = bc->stepList(BUILDSTEPS_CLEAN);
+ BuildStepList *cleanSteps = bc->stepList(QLatin1String(BUILDSTEPS_CLEAN));
MakeStep *cleanMakeStep = new MakeStep(cleanSteps);
cleanMakeStep->setAdditionalArguments(QLatin1String("clean"));
cleanMakeStep->setClean(true);
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp
index 132f805937..92603861af 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp
+++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp
@@ -48,7 +48,7 @@
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <extensionsystem/pluginmanager.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
@@ -148,11 +148,6 @@ AutotoolsTarget *AutotoolsProject::activeTarget() const
return static_cast<AutotoolsTarget *>(Project::activeTarget());
}
-QList<Project *> AutotoolsProject::dependsOn()
-{
- return QList<Project *>();
-}
-
QString AutotoolsProject::defaultBuildDirectory() const
{
return projectDirectory();
@@ -317,7 +312,7 @@ void AutotoolsProject::makefileParsingFinished()
// Add configure.ac file to project and watch for changes.
const QLatin1String configureAc(QLatin1String("configure.ac"));
- const QFile configureAcFile(fileInfo.absolutePath() + QChar('/') + configureAc);
+ const QFile configureAcFile(fileInfo.absolutePath() + QLatin1Char('/') + configureAc);
if (configureAcFile.exists()) {
files.append(configureAc);
const QString configureAcFilePath = dir.absoluteFilePath(configureAc);
@@ -368,13 +363,10 @@ void AutotoolsProject::buildFileNodeTree(const QDir &directory,
if (file.contains(QLatin1String(".moc")))
continue;
- QString subDir = baseDir + QChar('/') + file;
- for (int i = subDir.length() - 1; i >= 0; --i) {
- if (subDir.at(i) == QChar('/')) {
- subDir = subDir.left(i);
- break;
- }
- }
+ QString subDir = baseDir + QLatin1Char('/') + file;
+ const int lastSlashPos = subDir.lastIndexOf(QLatin1Char('/'));
+ if (lastSlashPos != -1)
+ subDir.truncate(lastSlashPos);
// Add folder nodes, that are not already available
oldParentFolder = parentFolder;
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.h b/src/plugins/autotoolsprojectmanager/autotoolsproject.h
index 5524e012b7..b737e11ebb 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsproject.h
+++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.h
@@ -84,7 +84,6 @@ public:
QList<ProjectExplorer::BuildConfigWidget*> subConfigWidgets();
ProjectExplorer::ProjectNode *rootProjectNode() const;
QStringList files(FilesMode fileMode) const;
- QList<ProjectExplorer::Project *> dependsOn();
QString defaultBuildDirectory() const;
QStringList buildTargets() const;
ProjectExplorer::ToolChain *toolChain() const;
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.pro b/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.pro
index b642847aa7..b428d54a28 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.pro
+++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.pro
@@ -1,6 +1,7 @@
TEMPLATE = lib
TARGET = AutotoolsProjectManager
#PROVIDER = Openismus
+DEFINES += QT_NO_CAST_FROM_ASCII
include(../../qtcreatorplugin.pri)
include(autotoolsprojectmanager_dependencies.pri)
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h b/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h
index e7210d0ac3..2e46de1746 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h
+++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h
@@ -70,6 +70,9 @@ namespace Internal {
class AutotoolsProjectPlugin : public ExtensionSystem::IPlugin
{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AutotoolsProjectManager.json")
+
public:
AutotoolsProjectPlugin();
diff --git a/src/plugins/autotoolsprojectmanager/autotoolstarget.cpp b/src/plugins/autotoolsprojectmanager/autotoolstarget.cpp
index 1ed7d5de70..431a9e6367 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolstarget.cpp
+++ b/src/plugins/autotoolsprojectmanager/autotoolstarget.cpp
@@ -146,10 +146,10 @@ AutotoolsTarget *AutotoolsTargetFactory::create(Project *parent, const QString &
// Add default build configuration:
AutotoolsBuildConfigurationFactory *bcf = t->buildConfigurationFactory();
AutotoolsBuildConfiguration *bc = bcf->createDefaultConfiguration(t);
- bc->setDisplayName("Default Build");
+ bc->setDisplayName(tr("Default Build"));
t->addBuildConfiguration(bc);
- t->addDeployConfiguration(t->createDeployConfiguration(ProjectExplorer::Constants::DEFAULT_DEPLOYCONFIGURATION_ID));
+ t->addDeployConfiguration(t->createDeployConfiguration(QLatin1String(ProjectExplorer::Constants::DEFAULT_DEPLOYCONFIGURATION_ID)));
// User needs to choose where the executable file is.
// TODO: Parse the file in *Anjuta style* to be able to add custom RunConfigurations.
t->addRunConfiguration(new CustomExecutableRunConfiguration(t));
diff --git a/src/plugins/autotoolsprojectmanager/configurestep.cpp b/src/plugins/autotoolsprojectmanager/configurestep.cpp
index 1fce702ca8..095911a326 100644
--- a/src/plugins/autotoolsprojectmanager/configurestep.cpp
+++ b/src/plugins/autotoolsprojectmanager/configurestep.cpp
@@ -84,7 +84,7 @@ bool ConfigureStepFactory::canCreate(BuildStepList *parent, const QString &id) c
if (parent->target()->project()->id() != QLatin1String(Constants::AUTOTOOLS_PROJECT_ID))
return false;
- if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_BUILD)
+ if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_BUILD))
return false;
return QLatin1String(CONFIGURE_STEP_ID) == id;
@@ -167,7 +167,7 @@ bool ConfigureStep::init()
pp->setMacroExpander(bc->macroExpander());
pp->setEnvironment(bc->environment());
pp->setWorkingDirectory(bc->buildDirectory());
- pp->setCommand("configure");
+ pp->setCommand(QLatin1String("configure"));
pp->setArguments(additionalArguments());
return AbstractProcessStep::init();
@@ -280,7 +280,7 @@ void ConfigureStepConfigWidget::updateDetails()
param.setMacroExpander(bc->macroExpander());
param.setEnvironment(bc->environment());
param.setWorkingDirectory(bc->buildDirectory());
- param.setCommand("configure");
+ param.setCommand(QLatin1String("configure"));
param.setArguments(m_configureStep->additionalArguments());
m_summaryText = param.summary(displayName());
emit updateSummary();
diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
index 0bee440343..c87ca62b46 100644
--- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp
+++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
@@ -134,7 +134,7 @@ MakefileParser::TopTarget MakefileParser::topTarget() const
TopTarget topTarget = Undefined;
const QString line = m_line.simplified();
- if (!line.isEmpty() && !line.startsWith(QChar('#'))) {
+ if (!line.isEmpty() && !line.startsWith(QLatin1Char('#'))) {
// TODO: Check how many fixed strings like AM_DEFAULT_SOURCE_EXT will
// be needed vs. variable strings like _SOURCES. Dependent on this a
// more clever way than this (expensive) if-cascading might be done.
@@ -236,7 +236,7 @@ void MakefileParser::parseSubDirs()
QStringList::iterator it = subDirs.begin();
while (it != subDirs.end()) {
// Erase all entries that represent a '.'
- if ((*it) == QChar('.')) {
+ if ((*it) == QLatin1String(".")) {
hasDotSubDir = true;
it = subDirs.erase(it);
} else {
@@ -256,7 +256,7 @@ void MakefileParser::parseSubDirs()
// Delegate the parsing of all sub directories to a local
// makefile parser and merge the results
foreach (const QString& subDir, subDirs) {
- const QChar slash('/');
+ const QChar slash = QLatin1Char('/');
const QString subDirMakefile = path + slash + subDir
+ slash + makefileName;
@@ -317,7 +317,7 @@ QStringList MakefileParser::directorySources(const QString &directory,
extensions);
const QString dirPath = info.fileName();
foreach (const QString& subDirSource, subDirSources)
- list.append(dirPath + QChar('/') + subDirSource);
+ list.append(dirPath + QLatin1Char('/') + subDirSource);
} else {
// Check whether the file matches to an extension
foreach (const QString& extension, extensions) {
@@ -339,7 +339,7 @@ QStringList MakefileParser::targetValues(bool *hasVariables)
if (hasVariables != 0)
*hasVariables = false;
- const int index = m_line.indexOf(QChar('='));
+ const int index = m_line.indexOf(QLatin1Char('='));
if (index < 0) {
m_success = false;
return QStringList();
@@ -354,7 +354,7 @@ QStringList MakefileParser::targetValues(bool *hasVariables)
// Get all values of a line separated by spaces.
// Values representing a variable like $(value) get
// removed currently.
- QStringList lineValues = m_line.split(QChar(' '));
+ QStringList lineValues = m_line.split(QLatin1Char(' '));
QStringList::iterator it = lineValues.begin();
while (it != lineValues.end()) {
if ((*it).startsWith(QLatin1String("$("))) {
@@ -368,7 +368,7 @@ QStringList MakefileParser::targetValues(bool *hasVariables)
endReached = lineValues.isEmpty();
if (!endReached) {
- const QChar backSlash('\\');
+ const QChar backSlash = QLatin1Char('\\');
QString last = lineValues.last();
if (last.endsWith(backSlash)) {
// The last value contains a backslash. Remove the
diff --git a/src/plugins/autotoolsprojectmanager/makestep.cpp b/src/plugins/autotoolsprojectmanager/makestep.cpp
index 57c3ef834a..e44eee9fe1 100644
--- a/src/plugins/autotoolsprojectmanager/makestep.cpp
+++ b/src/plugins/autotoolsprojectmanager/makestep.cpp
@@ -87,7 +87,7 @@ bool MakeStepFactory::canCreate(BuildStepList *parent, const QString &id) const
if (parent->target()->project()->id() != QLatin1String(AUTOTOOLS_PROJECT_ID))
return false;
- if (parent->id() != BUILDSTEPS_BUILD)
+ if (parent->id() != QLatin1String(BUILDSTEPS_BUILD))
return false;
return QLatin1String(MAKE_STEP_ID) == id;
diff --git a/src/plugins/bazaar/annotationhighlighter.cpp b/src/plugins/bazaar/annotationhighlighter.cpp
index 6ea7fd7108..ed1d7b0980 100644
--- a/src/plugins/bazaar/annotationhighlighter.cpp
+++ b/src/plugins/bazaar/annotationhighlighter.cpp
@@ -40,7 +40,7 @@ BazaarAnnotationHighlighter::BazaarAnnotationHighlighter(const ChangeNumbers &ch
const QColor &bg,
QTextDocument *document)
: VcsBase::BaseAnnotationHighlighter(changeNumbers, bg, document),
- m_changeset(QLatin1String(Constants::CHANGESET_ID))
+ m_changeset(QLatin1String(Constants::ANNOTATE_CHANGESET_ID))
{
}
diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp
index d148d07623..9257fa9fa9 100644
--- a/src/plugins/bazaar/bazaarclient.cpp
+++ b/src/plugins/bazaar/bazaarclient.cpp
@@ -140,6 +140,16 @@ QString BazaarClient::vcsEditorKind(VcsCommand cmd) const
}
}
+QString BazaarClient::vcsCommandString(VcsCommand cmd) const
+{
+ switch (cmd) {
+ case CloneCommand:
+ return QLatin1String("branch");
+ default:
+ return VcsBaseClient::vcsCommandString(cmd);
+ }
+}
+
QStringList BazaarClient::revisionSpec(const QString &revision) const
{
QStringList args;
diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h
index cdd4137871..2653a9dc36 100644
--- a/src/plugins/bazaar/bazaarclient.h
+++ b/src/plugins/bazaar/bazaarclient.h
@@ -64,6 +64,7 @@ public:
protected:
QString vcsEditorKind(VcsCommand cmd) const;
+ QString vcsCommandString(VcsCommand cmd) const;
QStringList revisionSpec(const QString &revision) const;
VcsBase::VcsBaseEditorParameterWidget *createDiffEditor(const QString &workingDir,
const QStringList &files,
diff --git a/src/plugins/bazaar/bazaareditor.cpp b/src/plugins/bazaar/bazaareditor.cpp
index b122152244..2a52248c82 100644
--- a/src/plugins/bazaar/bazaareditor.cpp
+++ b/src/plugins/bazaar/bazaareditor.cpp
@@ -55,6 +55,7 @@ using namespace Bazaar;
BazaarEditor::BazaarEditor(const VcsBase::VcsBaseEditorParameters *type, QWidget *parent)
: VcsBase::VcsBaseEditorWidget(type, parent),
+ m_changesetId(QLatin1String(Constants::CHANGESET_ID)),
m_exactChangesetId(QLatin1String(Constants::CHANGESET_ID_EXACT)),
m_diffFileId(QLatin1String("^=== [a-z]+ [a-z]+ '(.*)'\\s*"))
{
@@ -86,12 +87,30 @@ QSet<QString> BazaarEditor::annotationChanges() const
QString BazaarEditor::changeUnderCursor(const QTextCursor &cursorIn) const
{
+ // The test is done in two steps: first we check if the line contains a
+ // changesetId. Then we check if the cursor is over the changesetId itself
+ // and not over "revno" or another part of the line.
+ // The two steps are necessary because matching only for the changesetId
+ // leads to many false-positives (a regex like "[0-9]+" matches a lot of text).
+ const int cursorCol = cursorIn.columnNumber();
QTextCursor cursor = cursorIn;
- cursor.select(QTextCursor::WordUnderCursor);
+ cursor.select(QTextCursor::LineUnderCursor);
if (cursor.hasSelection()) {
- const QString change = cursor.selectedText();
- if (m_exactChangesetId.exactMatch(change))
- return change;
+ const QString line = cursor.selectedText();
+ const int start = m_changesetId.indexIn(line);
+ if (start > -1) {
+ const QString match = m_changesetId.cap(0);
+ const int stop = start + match.length();
+ if (start <= cursorCol && cursorCol <= stop) {
+ cursor = cursorIn;
+ cursor.select(QTextCursor::WordUnderCursor);
+ if (cursor.hasSelection()) {
+ const QString change = cursor.selectedText();
+ if (m_exactChangesetId.exactMatch(change))
+ return change;
+ }
+ }
+ }
}
return QString();
}
diff --git a/src/plugins/bazaar/bazaareditor.h b/src/plugins/bazaar/bazaareditor.h
index 0c9198509f..4538c70043 100644
--- a/src/plugins/bazaar/bazaareditor.h
+++ b/src/plugins/bazaar/bazaareditor.h
@@ -54,6 +54,7 @@ private:
virtual VcsBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes, const QColor &bg) const;
virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileSpec) const;
+ const QRegExp m_changesetId;
const QRegExp m_exactChangesetId;
const QRegExp m_diffFileId;
};
diff --git a/src/plugins/bazaar/bazaarplugin.h b/src/plugins/bazaar/bazaarplugin.h
index dff7611977..f64372c814 100644
--- a/src/plugins/bazaar/bazaarplugin.h
+++ b/src/plugins/bazaar/bazaarplugin.h
@@ -81,6 +81,7 @@ class BazaarEditor;
class BazaarPlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Bazaar.json")
public:
BazaarPlugin();
diff --git a/src/plugins/bazaar/constants.h b/src/plugins/bazaar/constants.h
index ccccc2e1fe..12560b1777 100644
--- a/src/plugins/bazaar/constants.h
+++ b/src/plugins/bazaar/constants.h
@@ -41,8 +41,13 @@ const char BAZAARREPO[] = ".bzr";
const char BAZAARDEFAULT[] = "bzr";
// Changeset identifiers
-const char CHANGESET_ID[] = "([0-9]+)"; // match and capture
-const char CHANGESET_ID_EXACT[] = "[0-9]+"; // match
+const char CHANGESET_ID[] = "^("
+ "revno: [.0-9]+" // detailed
+ "| +[.0-9]+" // short
+ "|[.0-9]+: " // line
+ ")";
+const char CHANGESET_ID_EXACT[] = "([.0-9]+)";
+const char ANNOTATE_CHANGESET_ID[] = "([.0-9]+)";
// Base editor parameters
const char COMMANDLOG_ID[] = "Bazaar Command Log Editor";
diff --git a/src/plugins/bineditor/bineditorplugin.h b/src/plugins/bineditor/bineditorplugin.h
index 2db7c38001..b4fd405089 100644
--- a/src/plugins/bineditor/bineditorplugin.h
+++ b/src/plugins/bineditor/bineditorplugin.h
@@ -60,6 +60,7 @@ class BinEditorFactory;
class BinEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "BinEditor.json")
public:
BinEditorPlugin();
diff --git a/src/plugins/bookmarks/bookmarksplugin.h b/src/plugins/bookmarks/bookmarksplugin.h
index 2607ebcb21..7f03ce93b8 100644
--- a/src/plugins/bookmarks/bookmarksplugin.h
+++ b/src/plugins/bookmarks/bookmarksplugin.h
@@ -59,6 +59,7 @@ class BookmarkManager;
class BookmarksPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Bookmarks.json")
public:
BookmarksPlugin();
diff --git a/src/plugins/classview/classview_dependencies.pri b/src/plugins/classview/classview_dependencies.pri
index 19ddab5e9a..b130241048 100644
--- a/src/plugins/classview/classview_dependencies.pri
+++ b/src/plugins/classview/classview_dependencies.pri
@@ -2,5 +2,4 @@ include(../../libs/utils/utils.pri)
include(../../plugins/coreplugin/coreplugin.pri)
include(../../plugins/cpptools/cpptools.pri)
include(../../plugins/projectexplorer/projectexplorer.pri)
-include(../../libs/cplusplus/cplusplus.pri)
include(../../plugins/texteditor/texteditor.pri)
diff --git a/src/plugins/classview/classviewmanager.cpp b/src/plugins/classview/classviewmanager.cpp
index e3e78ccbff..d2bedb4f42 100644
--- a/src/plugins/classview/classviewmanager.cpp
+++ b/src/plugins/classview/classviewmanager.cpp
@@ -40,7 +40,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <texteditor/basetexteditor.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
diff --git a/src/plugins/classview/classviewparser.cpp b/src/plugins/classview/classviewparser.cpp
index 0056918976..2eff6ab8e3 100644
--- a/src/plugins/classview/classviewparser.cpp
+++ b/src/plugins/classview/classviewparser.cpp
@@ -41,7 +41,7 @@
#include <Name.h>
// other
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/Overview.h>
#include <cplusplus/Icons.h>
#include <projectexplorer/projectexplorer.h>
diff --git a/src/plugins/classview/classviewparser.h b/src/plugins/classview/classviewparser.h
index 0706991860..a74d7e4360 100644
--- a/src/plugins/classview/classviewparser.h
+++ b/src/plugins/classview/classviewparser.h
@@ -38,7 +38,7 @@
#include "classviewparsertreeitem.h"
#include <CPlusPlusForwardDeclarations.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
// might be changed to forward declaration - is not done to be less dependent
diff --git a/src/plugins/classview/classviewplugin.h b/src/plugins/classview/classviewplugin.h
index 912856b064..3a0f8d455a 100644
--- a/src/plugins/classview/classviewplugin.h
+++ b/src/plugins/classview/classviewplugin.h
@@ -46,6 +46,7 @@ namespace Internal {
class Plugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClassView.json")
public:
//! Constructor
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
index a0586f9168..0f970bfef6 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
@@ -37,7 +37,10 @@
#include "cmakeprojectconstants.h"
#include "cmakeproject.h"
+#include <coreplugin/icore.h>
#include <coreplugin/infobar.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <texteditor/fontsettings.h>
@@ -72,6 +75,11 @@ Core::IEditor *CMakeEditor::duplicate(QWidget *parent)
return ret->editor();
}
+Core::Context CMakeEditor::context() const
+{
+ return Core::Context(Constants::C_CMAKEEDITOR);
+}
+
Core::Id CMakeEditor::id() const
{
return CMakeProjectManager::Constants::CMAKE_EDITOR_ID;
@@ -116,9 +124,12 @@ CMakeEditorWidget::CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factor
doc->setMimeType(QLatin1String(CMakeProjectManager::Constants::CMAKEMIMETYPE));
setBaseTextDocument(doc);
- ah->setupActions(this);
-
baseTextDocument()->setSyntaxHighlighter(new CMakeHighlighter);
+
+ m_commentDefinition.clearCommentStyles();
+ m_commentDefinition.setSingleLine(QLatin1String("#"));
+
+ ah->setupActions(this);
}
TextEditor::BaseTextEditor *CMakeEditorWidget::createEditor()
@@ -126,6 +137,28 @@ TextEditor::BaseTextEditor *CMakeEditorWidget::createEditor()
return new CMakeEditor(this);
}
+void CMakeEditorWidget::unCommentSelection()
+{
+ Utils::unCommentSelection(this, m_commentDefinition);
+}
+
+void CMakeEditorWidget::contextMenuEvent(QContextMenuEvent *e)
+{
+ QMenu *menu = new QMenu();
+
+ Core::ActionManager *am = Core::ICore::instance()->actionManager();
+ Core::ActionContainer *mcontext = am->actionContainer(Constants::M_CONTEXT);
+ QMenu *contextMenu = mcontext->menu();
+
+ foreach (QAction *action, contextMenu->actions())
+ menu->addAction(action);
+
+ appendStandardContextMenuActions(menu);
+
+ menu->exec(e->globalPos());
+ delete menu;
+}
+
void CMakeEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
{
TextEditor::BaseTextEditorWidget::setFontSettings(fs);
@@ -147,8 +180,85 @@ void CMakeEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
highlighter->rehighlight();
}
+static bool isValidFileNameChar(const QChar &c)
+{
+ if (c.isLetterOrNumber()
+ || c == QLatin1Char('.')
+ || c == QLatin1Char('_')
+ || c == QLatin1Char('-')
+ || c == QLatin1Char('/')
+ || c == QLatin1Char('\\'))
+ return true;
+ return false;
+}
+
+CMakeEditorWidget::Link CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
+ bool/* resolveTarget*/)
+{
+ Link link;
+
+ int lineNumber = 0, positionInBlock = 0;
+ convertPosition(cursor.position(), &lineNumber, &positionInBlock);
+
+ const QString block = cursor.block().text();
+
+ // check if the current position is commented out
+ const int hashPos = block.indexOf(QLatin1Char('#'));
+ if (hashPos >= 0 && hashPos < positionInBlock)
+ return link;
+
+ // find the beginning of a filename
+ QString buffer;
+ int beginPos = positionInBlock - 1;
+ while (beginPos >= 0) {
+ QChar c = block.at(beginPos);
+ if (isValidFileNameChar(c)) {
+ buffer.prepend(c);
+ beginPos--;
+ } else {
+ break;
+ }
+ }
+
+ // find the end of a filename
+ int endPos = positionInBlock;
+ while (endPos < block.count()) {
+ QChar c = block.at(endPos);
+ if (isValidFileNameChar(c)) {
+ buffer.append(c);
+ endPos++;
+ } else {
+ break;
+ }
+ }
+
+ if (buffer.isEmpty())
+ return link;
+
+ // TODO: Resolve variables
+
+ QDir dir(QFileInfo(editorDocument()->fileName()).absolutePath());
+ QString fileName = dir.filePath(buffer);
+ QFileInfo fi(fileName);
+ if (fi.exists()) {
+ if (fi.isDir()) {
+ QDir subDir(fi.absoluteFilePath());
+ QString subProject = subDir.filePath("CMakeLists.txt");
+ if (QFileInfo(subProject).exists())
+ fileName = subProject;
+ else
+ return link;
+ }
+ link.fileName = fileName;
+ link.begin = cursor.position() - positionInBlock + beginPos + 1;
+ link.end = cursor.position() - positionInBlock + endPos;
+ }
+ return link;
+}
+
+
//
-// ProFileDocument
+// CMakeDocument
//
CMakeDocument::CMakeDocument()
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.h b/src/plugins/cmakeprojectmanager/cmakeeditor.h
index 4a89cced20..97fe2529f9 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditor.h
+++ b/src/plugins/cmakeprojectmanager/cmakeeditor.h
@@ -37,6 +37,7 @@
#include <texteditor/basetextdocument.h>
#include <texteditor/basetexteditor.h>
+#include <utils/uncommentselection.h>
namespace TextEditor {
@@ -59,6 +60,7 @@ public:
bool duplicateSupported() const { return true; }
Core::IEditor *duplicate(QWidget *parent);
+ Core::Context context() const;
Core::Id id() const;
bool isTemporary() const { return false; }
@@ -81,15 +83,21 @@ public:
CMakeEditorFactory *factory() { return m_factory; }
TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; }
+ Link findLinkAt(const QTextCursor &cursor,
+ bool resolveTarget = true);
+
protected:
TextEditor::BaseTextEditor *createEditor();
+ void contextMenuEvent(QContextMenuEvent *e);
public slots:
- virtual void setFontSettings(const TextEditor::FontSettings &);
+ void unCommentSelection();
+ void setFontSettings(const TextEditor::FontSettings &);
private:
CMakeEditorFactory *m_factory;
TextEditor::TextEditorActionHandler *m_ah;
+ Utils::CommentDefinition m_commentDefinition;
};
class CMakeDocument : public TextEditor::BaseTextDocument
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp
index c5b8ee9053..f054f6f066 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp
@@ -34,18 +34,45 @@
#include "cmakeprojectconstants.h"
#include "cmakeeditor.h"
+#include <coreplugin/icore.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <texteditor/texteditoractionhandler.h>
+#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
-CMakeEditorFactory::CMakeEditorFactory(CMakeManager *manager, TextEditor::TextEditorActionHandler *handler)
+CMakeEditorFactory::CMakeEditorFactory(CMakeManager *manager)
: m_mimeTypes(QStringList() << QLatin1String(CMakeProjectManager::Constants::CMAKEMIMETYPE)),
- m_manager(manager),
- m_actionHandler(handler)
+ m_manager(manager)
{
+ using namespace Core;
+ using namespace TextEditor;
+ m_actionHandler =
+ new TextEditorActionHandler(Constants::C_CMAKEEDITOR,
+ TextEditorActionHandler::UnCommentSelection
+ | TextEditorActionHandler::JumpToFileUnderCursor);
+
+ ICore *core = ICore::instance();
+ ActionManager *am = core->actionManager();
+ ActionContainer *contextMenu = am->createMenu(Constants::M_CONTEXT);
+ Command *cmd;
+ Context cmakeEditorContext = Context(Constants::C_CMAKEEDITOR);
+
+ cmd = am->command(TextEditor::Constants::JUMP_TO_FILE_UNDER_CURSOR);
+ contextMenu->addAction(cmd);
+
+ QAction *separator = new QAction(this);
+ separator->setSeparator(true);
+ contextMenu->addAction(am->registerAction(separator,
+ Id(Constants::SEPARATOR), cmakeEditorContext));
+
+ cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
+ contextMenu->addAction(cmd);
}
Core::Id CMakeEditorFactory::id() const
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h
index e23a654808..a58a4115d1 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h
+++ b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h
@@ -51,7 +51,7 @@ class CMakeEditorFactory : public Core::IEditorFactory
Q_OBJECT
public:
- CMakeEditorFactory(CMakeManager *parent, TextEditor::TextEditorActionHandler *handler);
+ CMakeEditorFactory(CMakeManager *parent);
// IEditorFactory
QStringList mimeTypes() const;
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index ba79dff82c..4c6ffd8d86 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -47,7 +47,7 @@
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/toolchain.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h>
#include <coreplugin/icore.h>
@@ -62,6 +62,7 @@
#include <QFormLayout>
#include <QMainWindow>
#include <QInputDialog>
+#include <QFileSystemWatcher>
using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
@@ -496,11 +497,6 @@ CMakeTarget *CMakeProject::activeTarget() const
return static_cast<CMakeTarget *>(Project::activeTarget());
}
-QList<ProjectExplorer::Project *> CMakeProject::dependsOn()
-{
- return QList<Project *>();
-}
-
QList<ProjectExplorer::BuildConfigWidget*> CMakeProject::subConfigWidgets()
{
QList<ProjectExplorer::BuildConfigWidget*> list;
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h
index f5bb915b80..778bc7fc9e 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.h
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.h
@@ -48,10 +48,13 @@
#include <coreplugin/editormanager/ieditor.h>
#include <QXmlStreamReader>
-#include <QFileSystemWatcher>
#include <QPushButton>
#include <QLineEdit>
+QT_BEGIN_NAMESPACE
+class QFileSystemWatcher;
+QT_END_NAMESPACE
+
namespace CMakeProjectManager {
namespace Internal {
@@ -86,8 +89,6 @@ public:
CMakeTarget *activeTarget() const;
- QList<ProjectExplorer::Project *> dependsOn(); //NBS TODO implement dependsOn
-
QList<ProjectExplorer::BuildConfigWidget*> subConfigWidgets();
ProjectExplorer::ProjectNode *rootProjectNode() const;
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
index 4c16602e1c..1b92962767 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
@@ -47,6 +47,12 @@ const char RUNCMAKECONTEXTMENU[] = "CMakeProject.RunCMakeContextMenu";
// Project
const char CMAKEPROJECT_ID[] = "CMakeProjectManager.CMakeProject";
+// Menu
+const char M_CONTEXT[] = "CMakeEditor.ContextMenu";
+
+// Actions
+const char SEPARATOR[] = "CMakeEditor.Separator";
+
} // namespace Constants
} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
index 9f0749e90e..46b1db69d9 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
@@ -67,10 +67,8 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *
addAutoReleasedObject(manager);
addAutoReleasedObject(new MakeStepFactory);
addAutoReleasedObject(new CMakeRunConfigurationFactory);
- TextEditor::TextEditorActionHandler *editorHandler
- = new TextEditor::TextEditorActionHandler(CMakeProjectManager::Constants::C_CMAKEEDITOR);
- addAutoReleasedObject(new CMakeEditorFactory(manager, editorHandler));
+ addAutoReleasedObject(new CMakeEditorFactory(manager));
addAutoReleasedObject(new CMakeTargetFactory);
addAutoReleasedObject(new CMakeLocatorFilter);
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h
index 913f1b2ced..f876807d59 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h
@@ -44,6 +44,7 @@ class CMakeProjectPlugin
: public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CMakeProjectManager.json")
public:
CMakeProjectPlugin();
diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
index 1f7ead2a78..4e04743482 100644
--- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
@@ -316,7 +316,7 @@ void CMakeRunConfiguration::setEnabled(bool b)
if (m_enabled == b)
return;
m_enabled = b;
- emit isEnabledChanged(isEnabled());
+ emit enabledChanged();
setDefaultDisplayName(defaultDisplayName());
}
diff --git a/src/plugins/cmakeprojectmanager/cmaketarget.cpp b/src/plugins/cmakeprojectmanager/cmaketarget.cpp
index 31d47abbf2..4b92f2360f 100644
--- a/src/plugins/cmakeprojectmanager/cmaketarget.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketarget.cpp
@@ -32,7 +32,6 @@
#include "cmaketarget.h"
-#include "cmakeopenprojectwizard.h"
#include "cmakeproject.h"
#include "cmakerunconfiguration.h"
#include "cmakebuildconfiguration.h"
diff --git a/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.cpp b/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.cpp
index fb1a727936..1a4e427cf9 100644
--- a/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.cpp
@@ -35,7 +35,7 @@
#include "cmaketarget.h"
#include "cmakebuildconfiguration.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <QProcess>
diff --git a/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.h b/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.h
index 2fe7cc0ffc..b04b9a5f8b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.h
+++ b/src/plugins/cmakeprojectmanager/cmakeuicodemodelsupport.h
@@ -33,7 +33,7 @@
#ifndef CMAKEUICODEMODELSUPPORT_H
#define CMAKEUICODEMODELSUPPORT_H
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cpptools/uicodecompletionsupport.h>
#include <QDateTime>
diff --git a/src/plugins/coreplugin/basefilewizard.cpp b/src/plugins/coreplugin/basefilewizard.cpp
index 2e19226150..7d7a156489 100644
--- a/src/plugins/coreplugin/basefilewizard.cpp
+++ b/src/plugins/coreplugin/basefilewizard.cpp
@@ -464,10 +464,17 @@ void BaseFileWizard::runWizard(const QString &path, QWidget *parent, const QStri
// Create dialog and run it. Ensure that the dialog is deleted when
// leaving the func, but not before the IFileWizardExtension::process
// has been called
+
+ WizardDialogParameters::DialogParameterFlags dialogParameterFlags;
+
+ if (flags().testFlag(ForceCapitalLetterForFileName))
+ dialogParameterFlags |= WizardDialogParameters::ForceCapitalLetterForFileName;
+
const QScopedPointer<QWizard> wizard(createWizardDialog(parent, WizardDialogParameters(path,
allExtensionPages,
platform,
- requiredFeatures())));
+ requiredFeatures(),
+ dialogParameterFlags)));
QTC_ASSERT(!wizard.isNull(), return);
GeneratedFiles files;
@@ -811,6 +818,8 @@ QWizard *StandardFileWizard::createWizardDialog(QWidget *parent,
const WizardDialogParameters &wizardDialogParameters) const
{
Utils::FileWizardDialog *standardWizardDialog = new Utils::FileWizardDialog(parent);
+ if (wizardDialogParameters.flags().testFlag(WizardDialogParameters::ForceCapitalLetterForFileName))
+ standardWizardDialog->setForceFirstCapitalLetterForFileName(true);
standardWizardDialog->setWindowTitle(tr("New %1").arg(displayName()));
setupWizard(standardWizardDialog);
standardWizardDialog->setPath(wizardDialogParameters.defaultPath());
diff --git a/src/plugins/coreplugin/basefilewizard.h b/src/plugins/coreplugin/basefilewizard.h
index 6f4fc76baa..4f424058a6 100644
--- a/src/plugins/coreplugin/basefilewizard.h
+++ b/src/plugins/coreplugin/basefilewizard.h
@@ -113,12 +113,19 @@ class CORE_EXPORT WizardDialogParameters
public:
typedef QList<QWizardPage *> WizardPageList;
+ enum DialogParameterEnum {
+ ForceCapitalLetterForFileName = 0x01
+ };
+ Q_DECLARE_FLAGS(DialogParameterFlags, DialogParameterEnum)
+
explicit WizardDialogParameters(const QString &defaultPath, const WizardPageList &extensionPages,
- const QString &platform, const Core::FeatureSet &requiredFeatures)
+ const QString &platform, const Core::FeatureSet &requiredFeatures,
+ DialogParameterFlags flags)
: m_defaultPath(defaultPath),
m_extensionPages(extensionPages),
m_selectedPlatform(platform),
- m_requiredFeatures(requiredFeatures) {}
+ m_requiredFeatures(requiredFeatures),
+ m_parameterFlags(flags) {}
QString defaultPath() const
{ return m_defaultPath; }
@@ -132,11 +139,15 @@ public:
Core::FeatureSet requiredFeatures() const
{ return m_requiredFeatures; }
+ DialogParameterFlags flags() const
+ { return m_parameterFlags; }
+
private:
QString m_defaultPath;
WizardPageList m_extensionPages;
QString m_selectedPlatform;
Core::FeatureSet m_requiredFeatures;
+ DialogParameterFlags m_parameterFlags;
};
class CORE_EXPORT BaseFileWizard : public IWizard
@@ -221,5 +232,6 @@ QList<WizardClass*> createMultipleBaseFileWizardInstances(const QList<BaseFileWi
} // namespace Core
Q_DECLARE_OPERATORS_FOR_FLAGS(Core::GeneratedFile::Attributes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Core::WizardDialogParameters::DialogParameterFlags)
#endif // BASEFILEWIZARD_H
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index 947abf800f..e8a444b3e5 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -45,6 +45,7 @@ class MainWindow;
class CorePlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
public:
CorePlugin();
diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro
index d7eb7b20b0..c32ec82c72 100644
--- a/src/plugins/coreplugin/coreplugin.pro
+++ b/src/plugins/coreplugin/coreplugin.pro
@@ -75,7 +75,6 @@ SOURCES += mainwindow.cpp \
dialogs/ioptionspage.cpp \
dialogs/iwizard.cpp \
settingsdatabase.cpp \
- eventfilteringmainwindow.cpp \
imode.cpp \
editormanager/systemeditor.cpp \
designmode.cpp \
@@ -170,7 +169,6 @@ HEADERS += mainwindow.h \
fileiconprovider.h \
mimedatabase.h \
settingsdatabase.h \
- eventfilteringmainwindow.h \
editormanager/systemeditor.h \
designmode.h \
editortoolbar.h \
@@ -213,7 +211,10 @@ win32 {
LIBS += -lole32
}
else:macx {
- OBJECTIVE_SOURCES += progressmanager/progressmanager_mac.mm
+ HEADERS += macfullscreen.h
+ OBJECTIVE_SOURCES += \
+ progressmanager/progressmanager_mac.mm \
+ macfullscreen.mm
LIBS += -framework AppKit
}
else:unix {
@@ -228,4 +229,3 @@ else:unix {
}
}
OTHER_FILES += editormanager/BinFiles.mimetypes.xml
-
diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs
index 53dde48347..6fa9516b78 100644
--- a/src/plugins/coreplugin/coreplugin.qbs
+++ b/src/plugins/coreplugin/coreplugin.qbs
@@ -45,8 +45,6 @@ QtcPlugin {
"editmode.h",
"editortoolbar.cpp",
"editortoolbar.h",
- "eventfilteringmainwindow.cpp",
- "eventfilteringmainwindow.h",
"externaltool.cpp",
"externaltool.h",
"externaltoolmanager.h",
diff --git a/src/plugins/coreplugin/dialogs/iwizard.h b/src/plugins/coreplugin/dialogs/iwizard.h
index 8347cd0252..fe7b2ee0ca 100644
--- a/src/plugins/coreplugin/dialogs/iwizard.h
+++ b/src/plugins/coreplugin/dialogs/iwizard.h
@@ -57,7 +57,8 @@ public:
};
Q_DECLARE_FLAGS(WizardKinds, WizardKind)
enum WizardFlag {
- PlatformIndependent = 0x01
+ PlatformIndependent = 0x01,
+ ForceCapitalLetterForFileName = 0x02
};
Q_DECLARE_FLAGS(WizardFlags, WizardFlag)
diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/plugins/coreplugin/fileiconprovider.cpp
index 458d7cfcca..4cb33223ee 100644
--- a/src/plugins/coreplugin/fileiconprovider.cpp
+++ b/src/plugins/coreplugin/fileiconprovider.cpp
@@ -58,8 +58,8 @@
own overlay icon handling (Mac/Windows).
The class is a singleton: It's instance can be accessed via the static instance() method.
- Plugins can register custom icons via registerIconSuffix(), and retrieve icons via the icon()
- method.
+ Plugins can register custom overlay icons via registerIconOverlayForSuffix(), and
+ retrieve icons via the icon() method.
The instance is explicitly deleted by the core plugin for destruction order reasons.
*/
diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp
index 87d2db42cf..c75caa48e9 100644
--- a/src/plugins/coreplugin/generalsettings.cpp
+++ b/src/plugins/coreplugin/generalsettings.cpp
@@ -220,19 +220,19 @@ void GeneralSettings::resetInterfaceColor()
m_page->colorButton->setColor(StyleHelper::DEFAULT_BASE_COLOR);
}
-#ifdef Q_OS_UNIX
void GeneralSettings::resetTerminal()
{
+#if defined(Q_OS_UNIX)
m_page->terminalEdit->setText(ConsoleProcess::defaultTerminalEmulator() + QLatin1String(" -e"));
+#endif
}
-#ifndef Q_OS_MAC
void GeneralSettings::resetFileBrowser()
{
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
m_page->externalFileBrowserEdit->setText(UnixUtils::defaultFileBrowser());
-}
-#endif
#endif
+}
void GeneralSettings::variableHelpDialogCreator(const QString &helpText)
@@ -257,12 +257,12 @@ void GeneralSettings::variableHelpDialogCreator(const QString &helpText)
}
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
void GeneralSettings::showHelpForFileBrowser()
{
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
variableHelpDialogCreator(UnixUtils::fileBrowserHelpText());
-}
#endif
+}
void GeneralSettings::resetLanguage()
{
diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h
index 66ccbeb814..e5b5220d74 100644
--- a/src/plugins/coreplugin/generalsettings.h
+++ b/src/plugins/coreplugin/generalsettings.h
@@ -67,13 +67,9 @@ public:
private slots:
void resetInterfaceColor();
void resetLanguage();
-#ifdef Q_OS_UNIX
-# ifndef Q_OS_MAC
void showHelpForFileBrowser();
void resetFileBrowser();
-# endif
void resetTerminal();
-#endif
private:
void variableHelpDialogCreator(const QString &helpText);
diff --git a/src/tools/qmlprofilertool/constants.h b/src/plugins/coreplugin/macfullscreen.h
index 24d2702751..3a8a99aeff 100644
--- a/src/tools/qmlprofilertool/constants.h
+++ b/src/plugins/coreplugin/macfullscreen.h
@@ -30,21 +30,22 @@
**
**************************************************************************/
-#ifndef CONSTANTS_H
-#define CONSTANTS_H
+#ifndef MACFULLSCREEN_H
+#define MACFULLSCREEN_H
-namespace Constants {
+#include "mainwindow.h"
-const char CMD_HELP[] ="help";
-const char CMD_HELP2[] = "h";
-const char CMD_HELP3[] = "?";
+namespace Core {
+namespace Internal {
+namespace MacFullScreen {
-const char CMD_RECORD[] ="record";
-const char CMD_RECORD2[] ="r";
+bool supportsFullScreen();
+// adds fullscreen button to window for lion
+void addFullScreen(Core::Internal::MainWindow *window);
+void toggleFullScreen(Core::Internal::MainWindow *window);
-const char CMD_QUIT[] ="quit";
-const char CMD_QUIT2[] = "q";
+} // MacFullScreen
+} // Internal
+} // Core
-} // Contants
-
-#endif // CONSTANTS_H
+#endif // MACFULLSCREEN_H
diff --git a/src/plugins/coreplugin/macfullscreen.mm b/src/plugins/coreplugin/macfullscreen.mm
new file mode 100644
index 0000000000..3392328e87
--- /dev/null
+++ b/src/plugins/coreplugin/macfullscreen.mm
@@ -0,0 +1,123 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "macfullscreen.h"
+
+#include <AppKit/NSView.h>
+#include <AppKit/NSWindow.h>
+#include <Foundation/NSNotification.h>
+
+#include <QSysInfo>
+#include <qglobal.h>
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6
+enum {
+ NSWindowCollectionBehaviorFullScreenPrimary = (1 << 7)
+};
+
+static const NSString *NSWindowDidEnterFullScreenNotification = @"NSWindowDidEnterFullScreenNotification";
+static const NSString *NSWindowDidExitFullScreenNotification = @"NSWindowDidExitFullScreenNotification";
+#endif
+
+@interface WindowObserver : NSObject {
+ Core::Internal::MainWindow *window;
+}
+
+- (id)initWithMainWindow:(Core::Internal::MainWindow *)w;
+
+- (void)notifyDidEnterFullScreen:(NSNotification *)notification;
+- (void)notifyDidExitFullScreen:(NSNotification *)notification;
+
+@end
+
+@implementation WindowObserver
+
+- (id)initWithMainWindow:(Core::Internal::MainWindow *)w;
+{
+ if ((self = [self init])) {
+ window = w;
+ }
+ return self;
+}
+
+- (void)notifyDidEnterFullScreen:(NSNotification *)notification
+{
+ Q_UNUSED(notification)
+ window->setIsFullScreen(true);
+}
+
+- (void)notifyDidExitFullScreen:(NSNotification* )notification
+{
+ Q_UNUSED(notification)
+ window->setIsFullScreen(false);
+}
+
+@end
+
+static WindowObserver *observer = nil;
+
+using namespace Core::Internal;
+
+bool MacFullScreen::supportsFullScreen()
+{
+#if QT_VERSION >= 0x040800
+ return QSysInfo::MacintoshVersion >= QSysInfo::MV_LION;
+#else
+ return QSysInfo::MacintoshVersion >= 0x0009; /* MV_LION not defined */
+#endif
+}
+
+void MacFullScreen::addFullScreen(MainWindow *window)
+{
+ if (supportsFullScreen()) {
+ NSView *nsview = (NSView *) window->winId();
+ NSWindow *nswindow = [nsview window];
+ [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+
+ if (observer == nil)
+ observer = [[WindowObserver alloc] initWithMainWindow:window];
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:observer selector:@selector(notifyDidEnterFullScreen:)
+ name:NSWindowDidEnterFullScreenNotification object:nswindow];
+ [nc addObserver:observer selector:@selector(notifyDidExitFullScreen:)
+ name:NSWindowDidExitFullScreenNotification object:nswindow];
+ }
+}
+
+void MacFullScreen::toggleFullScreen(MainWindow *window)
+{
+ if (supportsFullScreen()) {
+ NSView *nsview = (NSView *) window->winId();
+ NSWindow *nswindow = [nsview window];
+ [nswindow performSelector:@selector(toggleFullScreen:) withObject: nil];
+ }
+}
diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp
index 4397688d7b..c4123834e7 100644
--- a/src/plugins/coreplugin/mainwindow.cpp
+++ b/src/plugins/coreplugin/mainwindow.cpp
@@ -58,7 +58,6 @@
#include "shortcutsettings.h"
#include "vcsmanager.h"
#include "variablechooser.h"
-
#include "scriptmanager_p.h"
#include "settingsdialog.h"
#include "variablemanager.h"
@@ -75,6 +74,10 @@
#include "externaltoolmanager.h"
#include "editormanager/systemeditor.h"
+#if defined(Q_OS_MAC)
+#include "macfullscreen.h"
+#endif
+
#include <app/app_version.h>
#include <coreplugin/findplaceholder.h>
#include <coreplugin/icorelistener.h>
@@ -126,7 +129,7 @@ using namespace Core::Internal;
enum { debugMainWindow = 0 };
MainWindow::MainWindow() :
- EventFilteringMainWindow(),
+ Utils::AppMainWindow(),
m_coreImpl(new ICore(this)),
m_additionalContexts(Constants::C_GLOBAL),
m_settings(ExtensionSystem::PluginManager::instance()->settings()),
@@ -227,6 +230,10 @@ MainWindow::MainWindow() :
statusBar()->setProperty("p_styled", true);
setAcceptDrops(true);
+
+#if defined(Q_OS_MAC)
+ MacFullScreen::addFullScreen(this);
+#endif
}
void MainWindow::setSidebarVisible(bool visible)
@@ -262,6 +269,16 @@ void MainWindow::setPresentationModeEnabled(bool enabled)
m_actionManager->setPresentationModeEnabled(enabled);
}
+#ifdef Q_OS_MAC
+void MainWindow::setIsFullScreen(bool fullScreen)
+{
+ if (fullScreen)
+ m_toggleFullScreenAction->setText(tr("Exit Full Screen"));
+ else
+ m_toggleFullScreenAction->setText(tr("Enter Full Screen"));
+}
+#endif
+
MainWindow::~MainWindow()
{
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
@@ -772,14 +789,26 @@ void MainWindow::registerDefaultActions()
mwindow->addAction(cmd, Constants::G_WINDOW_VIEWS);
m_toggleSideBarAction->setEnabled(false);
-#ifndef Q_OS_MAC
+#if defined(Q_OS_MAC)
+ bool fullScreenCheckable = false;
+ QKeySequence fullScreenKeySequence(tr("Ctrl+Meta+F"));
+ const QString fullScreenActionText(tr("Enter Full Screen"));
+ if (MacFullScreen::supportsFullScreen()) {
+#else
+ bool fullScreenCheckable = true;
+ QKeySequence fullScreenKeySequence(tr("Ctrl+Shift+F11"));
+ const QString fullScreenActionText(tr("Full Screen"));
+#endif
// Full Screen Action
- m_toggleFullScreenAction = new QAction(tr("Full Screen"), this);
- m_toggleFullScreenAction->setCheckable(true);
+ m_toggleFullScreenAction = new QAction(fullScreenActionText, this);
+ m_toggleFullScreenAction->setCheckable(fullScreenCheckable);
cmd = am->registerAction(m_toggleFullScreenAction, Constants::TOGGLE_FULLSCREEN, globalContext);
- cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F11")));
+ cmd->setDefaultKeySequence(fullScreenKeySequence);
+ cmd->setAttribute(Command::CA_UpdateText); /* for Mac */
mwindow->addAction(cmd, Constants::G_WINDOW_SIZE);
connect(m_toggleFullScreenAction, SIGNAL(triggered(bool)), this, SLOT(setFullScreen(bool)));
+#ifdef Q_OS_MAC
+ }
#endif
// Window->Views
@@ -1352,6 +1381,10 @@ QPrinter *MainWindow::printer() const
void MainWindow::setFullScreen(bool on)
{
+#if defined(Q_OS_MAC)
+ Q_UNUSED(on)
+ MacFullScreen::toggleFullScreen(this);
+#else
if (bool(windowState() & Qt::WindowFullScreen) == on)
return;
@@ -1364,6 +1397,7 @@ void MainWindow::setFullScreen(bool on)
//menuBar()->show();
//statusBar()->show();
}
+#endif
}
// Display a warning with an additional button to open
diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h
index 86d77f7800..96a50729bf 100644
--- a/src/plugins/coreplugin/mainwindow.h
+++ b/src/plugins/coreplugin/mainwindow.h
@@ -33,10 +33,11 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include "eventfilteringmainwindow.h"
#include "icontext.h"
#include "icore.h"
+#include <utils/appmainwindow.h>
+
#include <QMap>
#include <QColor>
@@ -80,7 +81,7 @@ class StatusBarManager;
class VersionDialog;
class SystemEditor;
-class MainWindow : public EventFilteringMainWindow
+class MainWindow : public Utils::AppMainWindow
{
Q_OBJECT
@@ -126,6 +127,9 @@ public:
bool isPresentationModeEnabled();
void setPresentationModeEnabled(bool);
+#ifdef Q_OS_MAC
+ void setIsFullScreen(bool fullScreen);
+#endif
signals:
void windowActivated();
diff --git a/src/plugins/coreplugin/textdocument.cpp b/src/plugins/coreplugin/textdocument.cpp
index 78a3447d7d..377567180b 100644
--- a/src/plugins/coreplugin/textdocument.cpp
+++ b/src/plugins/coreplugin/textdocument.cpp
@@ -140,6 +140,13 @@ void TextDocument::setCodec(const QTextCodec *codec)
d->m_format.codec = codec;
}
+void TextDocument::switchUtf8Bom()
+{
+ if (debug)
+ qDebug() << Q_FUNC_INFO << this << "UTF-8 BOM: " << !d->m_format.hasUtf8Bom;
+ d->m_format.hasUtf8Bom = !d->m_format.hasUtf8Bom;
+}
+
/*!
\brief Returns the format obtained from the last call to read().
*/
diff --git a/src/plugins/coreplugin/textdocument.h b/src/plugins/coreplugin/textdocument.h
index 9fc5a89056..6c59c69fcb 100644
--- a/src/plugins/coreplugin/textdocument.h
+++ b/src/plugins/coreplugin/textdocument.h
@@ -55,6 +55,7 @@ public:
Utils::TextFileFormat format() const;
const QTextCodec *codec() const;
void setCodec(const QTextCodec *);
+ void switchUtf8Bom();
ReadResult read(const QString &fileName, QStringList *plainTextList, QString *errorString);
ReadResult read(const QString &fileName, QString *plainText, QString *errorString);
diff --git a/src/plugins/cpaster/cpasterplugin.h b/src/plugins/cpaster/cpasterplugin.h
index 4b50faa91c..f7c93526b5 100644
--- a/src/plugins/cpaster/cpasterplugin.h
+++ b/src/plugins/cpaster/cpasterplugin.h
@@ -63,6 +63,7 @@ public slots:
class CodepasterPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CodePaster.json")
public:
CodepasterPlugin();
diff --git a/src/plugins/cppeditor/cppcompleteswitch.cpp b/src/plugins/cppeditor/cppcompleteswitch.cpp
index c0deb7a5e2..d39831c404 100644
--- a/src/plugins/cppeditor/cppcompleteswitch.cpp
+++ b/src/plugins/cppeditor/cppcompleteswitch.cpp
@@ -103,7 +103,7 @@ public:
class Operation: public CppQuickFixOperation
{
public:
- Operation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
+ Operation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
int priority,
CompoundStatementAST *compoundStatement,
const QStringList &values)
@@ -156,7 +156,7 @@ static Enum *findEnum(const QList<LookupItem> &results,
return 0;
}
-static Enum *conditionEnum(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
+static Enum *conditionEnum(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
SwitchStatementAST *statement)
{
Block *block = statement->symbol;
@@ -173,7 +173,7 @@ static Enum *conditionEnum(const QSharedPointer<const Internal::CppQuickFixAssis
} // end of anonymous namespace
QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(
- const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
+ const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = interface->path();
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 1e7a3b891a..b18cc1dacb 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -49,7 +49,7 @@
#include <SymbolVisitor.h>
#include <TranslationUnit.h>
#include <cplusplus/ASTPath.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TypeOfExpression.h>
#include <cplusplus/Overview.h>
@@ -1485,11 +1485,6 @@ CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor,
return Link();
}
-void CPPEditorWidget::jumpToDefinition()
-{
- openLink(findLinkAt(textCursor()));
-}
-
Symbol *CPPEditorWidget::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
{
if (symbol->isFunction())
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 999e4dacc7..b9a75de2ce 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -36,7 +36,7 @@
#include "cppeditorenums.h"
#include "cppfunctiondecldeflink.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
#include <texteditor/basetexteditor.h>
@@ -208,7 +208,6 @@ public Q_SLOTS:
virtual void setTabSettings(const TextEditor::TabSettings &);
void setSortedOutline(bool sort);
void switchDeclarationDefinition();
- void jumpToDefinition();
void renameSymbolUnderCursor();
void renameUsages();
void findUsages();
diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro
index 1178b5f70b..5fc351a3b6 100644
--- a/src/plugins/cppeditor/cppeditor.pro
+++ b/src/plugins/cppeditor/cppeditor.pro
@@ -2,8 +2,6 @@ TEMPLATE = lib
TARGET = CppEditor
DEFINES += CPPEDITOR_LIBRARY
include(../../qtcreatorplugin.pri)
-include($$IDE_SOURCE_TREE/src/libs/utils/utils.pri)
-include($$IDE_SOURCE_TREE/src/libs/cplusplus/cplusplus.pri)
include(cppeditor_dependencies.pri)
HEADERS += cppplugin.h \
cppeditor.h \
diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h
index e2aeff9d68..6ad17f0fda 100644
--- a/src/plugins/cppeditor/cppeditorconstants.h
+++ b/src/plugins/cppeditor/cppeditorconstants.h
@@ -48,7 +48,6 @@ const char SEPARATOR[] = "CppEditor.Separator";
const char SEPARATOR2[] = "CppEditor.Separator2";
const char SEPARATOR3[] = "CppEditor.Separator3";
const char SEPARATOR4[] = "CppEditor.Separator4";
-const char JUMP_TO_DEFINITION[] = "CppEditor.JumpToDefinition";
const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel";
const int TYPE_HIERARCHY_PRIORITY = 700;
diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp
index faa723a215..56d9660997 100644
--- a/src/plugins/cppeditor/cppelementevaluator.cpp
+++ b/src/plugins/cppeditor/cppelementevaluator.cpp
@@ -42,8 +42,8 @@
#include <Scope.h>
#include <Symbol.h>
#include <Symbols.h>
-#include <TypeHierarchyBuilder.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/TypeHierarchyBuilder.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
index 2534df0d97..7f3ec478d2 100644
--- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
+++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
@@ -993,7 +993,7 @@ class ApplyDeclDefLinkOperation : public CppQuickFixOperation
{
public:
explicit ApplyDeclDefLinkOperation(
- const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
+ const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
const QSharedPointer<FunctionDeclDefLink> &link)
: CppQuickFixOperation(interface, 10)
, m_link(link)
diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp
index 6e6d3e30ee..0c1ed57da9 100644
--- a/src/plugins/cppeditor/cpphoverhandler.cpp
+++ b/src/plugins/cppeditor/cpphoverhandler.cpp
@@ -37,7 +37,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/helpmanager.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
diff --git a/src/plugins/cppeditor/cppinsertdecldef.cpp b/src/plugins/cppeditor/cppinsertdecldef.cpp
index 87cb345dba..ff15aba075 100644
--- a/src/plugins/cppeditor/cppinsertdecldef.cpp
+++ b/src/plugins/cppeditor/cppinsertdecldef.cpp
@@ -64,7 +64,7 @@ namespace {
class InsertDeclOperation: public CppQuickFixOperation
{
public:
- InsertDeclOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
+ InsertDeclOperation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
const QString &targetFileName, const Class *targetSymbol,
InsertionPointLocator::AccessSpec xsSpec,
const QString &decl)
@@ -151,7 +151,7 @@ Class *isMemberFunction(const LookupContext &context, Function *function)
} // anonymous namespace
QList<CppQuickFixOperation::Ptr> DeclFromDef::match(
- const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
+ const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = interface->path();
CppRefactoringFilePtr file = interface->currentFile();
@@ -229,7 +229,7 @@ namespace {
class InsertDefOperation: public CppQuickFixOperation
{
public:
- InsertDefOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
+ InsertDefOperation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
Declaration *decl, const InsertionLocation &loc)
: CppQuickFixOperation(interface, 0)
, m_decl(decl)
@@ -297,7 +297,7 @@ private:
} // anonymous namespace
QList<CppQuickFixOperation::Ptr> DefFromDecl::match(
- const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
+ const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = interface->path();
diff --git a/src/plugins/cppeditor/cppplugin.cpp b/src/plugins/cppeditor/cppplugin.cpp
index ca3476bd40..bfc49aafdf 100644
--- a/src/plugins/cppeditor/cppplugin.cpp
+++ b/src/plugins/cppeditor/cppplugin.cpp
@@ -57,7 +57,7 @@
#include <texteditor/texteditorplugin.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/texteditorconstants.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cpptoolssettings.h>
@@ -242,12 +242,7 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess
cmd = am->command(Core::Id(CppTools::Constants::SWITCH_HEADER_SOURCE));
contextMenu->addAction(cmd);
- QAction *jumpToDefinition = new QAction(tr("Follow Symbol Under Cursor"), this);
- cmd = am->registerAction(jumpToDefinition,
- Constants::JUMP_TO_DEFINITION, context, true);
- cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
- connect(jumpToDefinition, SIGNAL(triggered()),
- this, SLOT(jumpToDefinition()));
+ cmd = am->command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR);
contextMenu->addAction(cmd);
cppToolsMenu->addAction(cmd);
@@ -304,7 +299,8 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess
m_actionHandler = new TextEditor::TextEditorActionHandler(CppEditor::Constants::C_CPPEDITOR,
TextEditor::TextEditorActionHandler::Format
| TextEditor::TextEditorActionHandler::UnCommentSelection
- | TextEditor::TextEditorActionHandler::UnCollapseAll);
+ | TextEditor::TextEditorActionHandler::UnCollapseAll
+ | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
m_actionHandler->initializeActions();
@@ -355,14 +351,6 @@ void CppPlugin::switchDeclarationDefinition()
editor->switchDeclarationDefinition();
}
-void CppPlugin::jumpToDefinition()
-{
- Core::EditorManager *em = Core::EditorManager::instance();
- CPPEditorWidget *editor = qobject_cast<CPPEditorWidget*>(em->currentEditor()->widget());
- if (editor)
- editor->jumpToDefinition();
-}
-
void CppPlugin::renameSymbolUnderCursor()
{
Core::EditorManager *em = Core::EditorManager::instance();
diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h
index acc251e5cb..652c942d76 100644
--- a/src/plugins/cppeditor/cppplugin.h
+++ b/src/plugins/cppeditor/cppplugin.h
@@ -55,6 +55,7 @@ class CppQuickFixAssistProvider;
class CppPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CppEditor.json")
public:
CppPlugin();
@@ -82,7 +83,6 @@ public slots:
private slots:
void switchDeclarationDefinition();
- void jumpToDefinition();
void renameSymbolUnderCursor();
void onTaskStarted(const QString &type);
void onAllTasksFinished(const QString &type);
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 7833f99099..44292fe075 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -56,7 +56,7 @@
#include <cplusplus/DependencyTable.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppRewriter.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cpprefactoringchanges.h>
diff --git a/src/libs/cplusplus/ModelManagerInterface.cpp b/src/plugins/cpptools/ModelManagerInterface.cpp
index 18443186df..18443186df 100644
--- a/src/libs/cplusplus/ModelManagerInterface.cpp
+++ b/src/plugins/cpptools/ModelManagerInterface.cpp
diff --git a/src/libs/cplusplus/ModelManagerInterface.h b/src/plugins/cpptools/ModelManagerInterface.h
index fdcfa7a66d..b873e880fe 100644
--- a/src/libs/cplusplus/ModelManagerInterface.h
+++ b/src/plugins/cpptools/ModelManagerInterface.h
@@ -35,8 +35,7 @@
#include <cplusplus/CppDocument.h>
#include <languageutils/fakemetaobject.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/toolchain.h>
+#include "cpptools_global.h"
#include <QObject>
#include <QHash>
@@ -66,18 +65,20 @@ namespace CppTools {
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT CppModelManagerInterface : public QObject
+class CPPTOOLS_EXPORT CppModelManagerInterface : public QObject
{
Q_OBJECT
public:
enum Language { CXX, OBJC };
- class CPLUSPLUS_EXPORT ProjectPart
+ class CPPTOOLS_EXPORT ProjectPart
{
public:
ProjectPart()
- : qtVersion(UnknownQt)
+ : language(CXX)
+ , cxx11Enabled(false)
+ , qtVersion(UnknownQt)
{}
public: //attributes
@@ -87,7 +88,7 @@ public:
QStringList frameworkPaths;
QStringList precompiledHeaders;
Language language;
- ProjectExplorer::ToolChain::CompilerFlags flags;
+ bool cxx11Enabled;
enum QtVersion {
UnknownQt = -1,
NoQt = 0,
@@ -96,16 +97,13 @@ public:
};
QtVersion qtVersion;
- bool cpp0xEnabled() const
- { return flags == ProjectExplorer::ToolChain::STD_CXX11; }
-
bool objcEnabled() const
{ return language == CppModelManagerInterface::OBJC; }
typedef QSharedPointer<ProjectPart> Ptr;
};
- class CPLUSPLUS_EXPORT ProjectInfo
+ class CPPTOOLS_EXPORT ProjectInfo
{
public:
ProjectInfo()
@@ -155,7 +153,7 @@ public:
QByteArray m_defines;
};
- class CPLUSPLUS_EXPORT WorkingCopy
+ class CPPTOOLS_EXPORT WorkingCopy
{
public:
void insert(const QString &fileName, const QString &source, unsigned revision = 0)
diff --git a/src/libs/cplusplus/TypeHierarchyBuilder.cpp b/src/plugins/cpptools/TypeHierarchyBuilder.cpp
index 73eba4dc85..73eba4dc85 100644
--- a/src/libs/cplusplus/TypeHierarchyBuilder.cpp
+++ b/src/plugins/cpptools/TypeHierarchyBuilder.cpp
diff --git a/src/libs/cplusplus/TypeHierarchyBuilder.h b/src/plugins/cpptools/TypeHierarchyBuilder.h
index 1fd8a82016..93727f548e 100644
--- a/src/libs/cplusplus/TypeHierarchyBuilder.h
+++ b/src/plugins/cpptools/TypeHierarchyBuilder.h
@@ -36,6 +36,7 @@
#include "CppDocument.h"
#include "ModelManagerInterface.h"
#include "Overview.h"
+#include "cpptools_global.h"
#include <QList>
#include <QStringList>
@@ -43,7 +44,7 @@
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT TypeHierarchy
+class CPPTOOLS_EXPORT TypeHierarchy
{
friend class TypeHierarchyBuilder;
@@ -59,7 +60,7 @@ private:
QList<TypeHierarchy> _hierarchy;
};
-class CPLUSPLUS_EXPORT TypeHierarchyBuilder
+class CPPTOOLS_EXPORT TypeHierarchyBuilder
{
public:
TypeHierarchyBuilder(Symbol *symbol, const Snapshot &snapshot);
diff --git a/src/plugins/cpptools/abstracteditorsupport.cpp b/src/plugins/cpptools/abstracteditorsupport.cpp
index e6e3e20651..3fb2d76983 100644
--- a/src/plugins/cpptools/abstracteditorsupport.cpp
+++ b/src/plugins/cpptools/abstracteditorsupport.cpp
@@ -37,7 +37,7 @@
#include <cplusplus/Overview.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/ExpressionUnderCursor.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include "ModelManagerInterface.h"
#include <CoreTypes.h>
#include <Names.h>
#include <Symbols.h>
diff --git a/src/plugins/cpptools/cppcodeformatter.cpp b/src/plugins/cpptools/cppcodeformatter.cpp
index 50f2165659..be10cab616 100644
--- a/src/plugins/cpptools/cppcodeformatter.cpp
+++ b/src/plugins/cpptools/cppcodeformatter.cpp
@@ -229,9 +229,74 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
default: tryExpression(); break;
} break;
+ case lambda_instroducer_or_subscribtion:
+ switch (kind) {
+ case T_RBRACKET: turnInto(lambda_declarator_expected); break; // we can't determine exact kind of expression. Try again
+ case T_COMMA:
+ case T_EQUAL: turnInto(lambda_instroducer); break; // ',' or '=' inside brackets can be only whithin lambda capture list
+ case T_IDENTIFIER: // '&', id, 'this' are allowed both in the capture list and subscribtion
+ case T_AMPER:
+ case T_THIS: break;
+ default: leave(); leave(); tryExpression(m_currentState.at(m_currentState.size() - 1).type == declaration_start); break;
+ // any other symbol allowed only in subscribtion operator
+ } break;
+
+ case lambda_declarator_expected:
+ switch (kind) {
+ case T_LPAREN: turnInto(lambda_declarator_or_expression); break; // '(' just after ']'. We can't make decisioin here
+ case T_LBRACE: turnInto(substatement_open); break; // '{' just after ']' opens a lambda-compound statement
+ default:
+ if (m_currentState.size() >= 3 && m_currentState.at(m_currentState.size() - 3).type == declaration_start)
+ leave();
+
+ leave();
+ continue;
+ } break;
+
+ case lambda_instroducer:
+ switch (kind) {
+ case T_RBRACKET: turnInto(lambda_declarator); break;
+ } break;
+
+ case lambda_declarator_or_expression:
+ switch (kind) {
+ case T_LBRACE: turnInto(substatement_open); /*tryStatement();*/ break;
+ case T_RPAREN: turnInto(lambda_statement_expected); break;
+ case T_IDENTIFIER:
+ case T_SEMICOLON: leave(); continue;
+ default:
+ if (tryDeclaration()) {// We found the declaration within '()' so it is lambda declarator
+ leave();
+ turnInto(lambda_declarator);
+ break;
+ } else {
+ turnInto(expression);
+ enter(arglist_open);
+ continue;
+ }
+ } break;
+
+ case lambda_statement_expected:
+ switch (kind) {
+ case T_LBRACE: turnInto(substatement_open); /*tryStatement()*/; break;
+ case T_NOEXCEPT: // 'noexcept', 'decltype' and 'mutable' are only part of lambda declarator
+ case T_DECLTYPE:
+ case T_MUTABLE: turnInto(lambda_declarator); break;
+ case T_RBRACKET: // '[', ']' and '->' can be part of lambda declarator
+ case T_LBRACKET:
+ case T_ARROW: break;
+ default: leave(); continue;
+ } break;
+
+ case lambda_declarator:
+ switch (kind) {
+ case T_LBRACE: turnInto(substatement_open); /*tryStatement()*/; break;
+ } break;
+
case arglist_open:
switch (kind) {
case T_SEMICOLON: leave(true); break;
+ case T_LBRACE: enter(brace_list_open); break;
case T_RBRACE: leave(true); continue;
case T_RPAREN: leave(); break;
default: tryExpression(); break;
@@ -739,6 +804,9 @@ bool CodeFormatter::tryExpression(bool alsoExpression)
}
}
break;
+ case T_LBRACKET:
+ newState = lambda_instroducer_or_subscribtion;
+ break;
}
if (newState != -1) {
diff --git a/src/plugins/cpptools/cppcodeformatter.h b/src/plugins/cpptools/cppcodeformatter.h
index 8dab0dbc81..a7da67f57c 100644
--- a/src/plugins/cpptools/cppcodeformatter.h
+++ b/src/plugins/cpptools/cppcodeformatter.h
@@ -177,7 +177,16 @@ public: // must be public to make Q_GADGET introspection work
assign_open, // after an assignment token
expression, // after a '=' in a declaration_start once we're sure it's not '= {'
- assign_open_or_initializer // after a '=' in a declaration start
+ assign_open_or_initializer, // after a '=' in a declaration start
+
+ lambda_instroducer_or_subscribtion, // just after '[' or in cases '[]' and '[id]' when we're not sure in the exact kind of expression
+ lambda_declarator_expected, // just after ']' in lambda_introducer_or_subscribtion
+ lambda_declarator_or_expression, // just after '](' when previous state is 'lambda_instroducer_or_subscribtion'
+ lambda_statement_expected,
+ lambda_instroducer, // when '=', '&' or ',' occurred within '[]'
+ lambda_declarator, // just after ']' when previous state is lambda_introducer
+ lambda_statement // just after '{' when previous state is lambda_declarator or lambda_declarator_or_expression
+
};
Q_ENUMS(StateType)
diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp
index e7f41b2b73..c40b6306fc 100644
--- a/src/plugins/cpptools/cppcompletionassist.cpp
+++ b/src/plugins/cpptools/cppcompletionassist.cpp
@@ -79,7 +79,7 @@
using namespace CPlusPlus;
using namespace CppEditor;
using namespace CppTools;
-using namespace Internal;
+using namespace CppTools::Internal;
using namespace TextEditor;
namespace CppTools {
diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp
index 46f68f55a5..2c47a6b371 100644
--- a/src/plugins/cpptools/cppfindreferences.cpp
+++ b/src/plugins/cpptools/cppfindreferences.cpp
@@ -55,7 +55,7 @@
#include <Names.h>
#include <Scope.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/Overview.h>
#include <cplusplus/FindUsages.h>
@@ -562,7 +562,7 @@ public:
unsigned lineStart;
const QString &lineSource = matchingLine(use.begin(), source, &lineStart);
usages.append(Usage(fileName, lineSource, use.beginLine(),
- use.begin() - lineStart, use.length()));
+ use.begin() - lineStart, useMacro.name().length()));
}
}
@@ -646,8 +646,13 @@ void CppFindReferences::findMacroUses(const Macro &macro)
{
// ### FIXME: Encoding?
const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1();
- search->addResult(macro.fileName(), macro.line(),
- source.mid(macro.offset(), macro.length()), 0, macro.length());
+ int lineBegin = source.lastIndexOf('\n', macro.offset()) + 1;
+ int lineEnd = source.indexOf('\n', macro.offset());
+ if (lineEnd == -1)
+ lineEnd = source.length();
+ const QByteArray line = source.mid(lineBegin, lineEnd - lineBegin);
+ search->addResult(macro.fileName(), macro.line(), line,
+ line.indexOf(macro.name()), macro.name().length());
}
QFuture<Usage> result;
diff --git a/src/plugins/cpptools/cpphighlightingsupport.h b/src/plugins/cpptools/cpphighlightingsupport.h
index 6b80542037..4319ff9c4e 100644
--- a/src/plugins/cpptools/cpphighlightingsupport.h
+++ b/src/plugins/cpptools/cpphighlightingsupport.h
@@ -72,6 +72,8 @@ public:
virtual ~CppHighlightingSupportFactory() = 0;
virtual CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor) = 0;
+
+ virtual bool hightlighterHandlesDiagnostics() const = 0;
};
} // namespace CppTools
diff --git a/src/plugins/cpptools/cpphighlightingsupportinternal.h b/src/plugins/cpptools/cpphighlightingsupportinternal.h
index 52ab1f8118..16c81e983a 100644
--- a/src/plugins/cpptools/cpphighlightingsupportinternal.h
+++ b/src/plugins/cpptools/cpphighlightingsupportinternal.h
@@ -62,6 +62,9 @@ public:
virtual ~CppHighlightingSupportInternalFactory();
virtual CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor);
+
+ virtual bool hightlighterHandlesDiagnostics() const
+ { return false; }
};
} // namespace Internal
diff --git a/src/plugins/cpptools/cpplocalsymbols.cpp b/src/plugins/cpptools/cpplocalsymbols.cpp
index 0b62d58c25..7c84b5cac3 100644
--- a/src/plugins/cpptools/cpplocalsymbols.cpp
+++ b/src/plugins/cpptools/cpplocalsymbols.cpp
@@ -109,6 +109,8 @@ protected:
bool checkLocalUse(NameAST *nameAst, unsigned firstToken)
{
if (SimpleNameAST *simpleName = nameAst->asSimpleName()) {
+ if (tokenAt(simpleName->identifier_token).generated())
+ return false;
const Identifier *id = identifier(simpleName->identifier_token);
for (int i = _scopeStack.size() - 1; i != -1; --i) {
if (Symbol *member = _scopeStack.at(i)->find(id)) {
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index 4e3b4a8f07..ae79ec65a8 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -96,7 +96,7 @@
namespace CPlusPlus {
uint qHash(const CppModelManagerInterface::ProjectPart &p)
{
- uint h = qHash(p.defines) ^ p.language ^ p.flags;
+ uint h = qHash(p.defines) ^ p.language ^ ((int) p.cxx11Enabled);
foreach (const QString &i, p.includePaths)
h ^= qHash(i);
@@ -113,7 +113,7 @@ bool operator==(const CppModelManagerInterface::ProjectPart &p1,
return false;
if (p1.language != p2.language)
return false;
- if (p1.flags != p2.flags)
+ if (p1.cxx11Enabled != p2.cxx11Enabled)
return false;
if (p1.includePaths != p2.includePaths)
return false;
@@ -201,9 +201,10 @@ static const char pp_configuration[] =
"#define __forceinline inline\n";
#ifndef ICHECK_BUILD
-CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
+CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing)
: snapshot(modelManager->snapshot()),
m_modelManager(modelManager),
+ m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
preprocess(this, &env),
m_revision(0)
{
@@ -214,6 +215,7 @@ CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager)
: preprocess(this, &env),
+ m_dumpFileNameWhileParsing(false),
m_revision(0)
{
}
@@ -503,7 +505,7 @@ void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &m
return;
m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine,
- QVector<MacroArgumentReference>(), true);
+ QVector<MacroArgumentReference>());
}
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name)
@@ -517,7 +519,6 @@ void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArr
void CppPreprocessor::startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
- bool inCondition,
const QVector<MacroArgumentReference> &actuals)
{
if (! m_currentDoc)
@@ -525,7 +526,7 @@ void CppPreprocessor::startExpandingMacro(unsigned offset,
//qDebug() << "start expanding:" << macro.name() << "text:" << originalText;
m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine,
- actuals, inCondition);
+ actuals);
}
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
@@ -600,7 +601,11 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
}
}
- //qDebug() << "parse file:" << fileName << "contents:" << contents.size();
+ if (m_dumpFileNameWhileParsing) {
+ qDebug() << "Parsing file:" << fileName
+// << "contents:" << contents.size()
+ ;
+ }
Document::Ptr doc = snapshot.document(fileName);
if (doc) {
@@ -620,6 +625,8 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
const QByteArray preprocessedCode = preprocess(fileName, contents);
+// { QByteArray b(preprocessedCode); b.replace("\n", "<<<\n"); qDebug("Preprocessed code for \"%s\": [[%s]]", fileName.toUtf8().constData(), b.constData()); }
+
doc->setUtf8Source(preprocessedCode);
doc->keepSourceAndAST();
doc->tokenize();
@@ -692,6 +699,7 @@ CppModelManager::CppModelManager(QObject *parent)
{
m_findReferences = new CppFindReferences(this);
m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
+ m_dumpFileNameWhileParsing = !qgetenv("QTCREATOR_DUMP_FILENAME_WHILE_PARSING").isNull();
m_revision = 0;
m_synchronizer.setCancelOnWait(true);
@@ -917,11 +925,12 @@ void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
#if 0
// Tons of debug output...
qDebug()<<"========= CppModelManager::updateProjectInfo ======";
- qDebug()<<" for project:"<< pinfo.project.data()->file()->fileName();
- foreach (const ProjectPart::Ptr &part, pinfo.projectParts) {
+ qDebug()<<" for project:"<< pinfo.project().data()->document()->fileName();
+ foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
qDebug() << "=== part ===";
qDebug() << "language:" << (part->language == CXX ? "C++" : "ObjC++");
- qDebug() << "compilerflags:" << part->flags;
+ qDebug() << "C++11:" << part->cxx11Enabled;
+ qDebug() << "Qt version:" << part->qtVersion;
qDebug() << "precompiled header:" << part->precompiledHeaders;
qDebug() << "defines:" << part->defines;
qDebug() << "includes:" << part->includePaths;
@@ -980,7 +989,7 @@ QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles
if (! sourceFiles.isEmpty() && m_indexerEnabled) {
const WorkingCopy workingCopy = buildWorkingCopyList();
- CppPreprocessor *preproc = new CppPreprocessor(this);
+ CppPreprocessor *preproc = new CppPreprocessor(this, m_dumpFileNameWhileParsing);
preproc->setRevision(++m_revision);
preproc->setProjectFiles(projectFiles());
preproc->setIncludePaths(includePaths());
@@ -1108,8 +1117,6 @@ void CppModelManager::updateEditor(Document::Ptr doc)
blockRanges.append(TextEditor::BaseTextEditorWidget::BlockRange(block.begin(), block.end()));
}
- QList<QTextEdit::ExtraSelection> selections;
-
// set up the format for the errors
QTextCharFormat errorFormat;
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
@@ -1120,47 +1127,51 @@ void CppModelManager::updateEditor(Document::Ptr doc)
warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
warningFormat.setUnderlineColor(Qt::darkYellow);
- QSet<int> lines;
- QList<Document::DiagnosticMessage> messages = doc->diagnosticMessages();
- messages += extraDiagnostics(doc->fileName());
- foreach (const Document::DiagnosticMessage &m, messages) {
- if (m.fileName() != fileName)
- continue;
- else if (lines.contains(m.line()))
- continue;
-
- lines.insert(m.line());
-
- QTextEdit::ExtraSelection sel;
- if (m.isWarning())
- sel.format = warningFormat;
- else
- sel.format = errorFormat;
-
- QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
- const QString text = c.block().text();
- for (int i = 0; i < text.size(); ++i) {
- if (! text.at(i).isSpace()) {
- c.setPosition(c.position() + i);
- break;
- }
- }
- c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
- sel.cursor = c;
- sel.format.setToolTip(m.text());
- selections.append(sel);
- }
-
QList<Editor> todo;
- foreach (const Editor &e, todo) {
+ foreach (const Editor &e, m_todo) {
if (e.textEditor != textEditor)
todo.append(e);
}
-
Editor e;
+
+ if (m_highlightingFactory->hightlighterHandlesDiagnostics()) {
+ e.updateSelections = false;
+ } else {
+ QSet<int> lines;
+ QList<Document::DiagnosticMessage> messages = doc->diagnosticMessages();
+ messages += extraDiagnostics(doc->fileName());
+ foreach (const Document::DiagnosticMessage &m, messages) {
+ if (m.fileName() != fileName)
+ continue;
+ else if (lines.contains(m.line()))
+ continue;
+
+ lines.insert(m.line());
+
+ QTextEdit::ExtraSelection sel;
+ if (m.isWarning())
+ sel.format = warningFormat;
+ else
+ sel.format = errorFormat;
+
+ QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
+ const QString text = c.block().text();
+ for (int i = 0; i < text.size(); ++i) {
+ if (! text.at(i).isSpace()) {
+ c.setPosition(c.position() + i);
+ break;
+ }
+ }
+ c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ sel.cursor = c;
+ sel.format.setToolTip(m.text());
+ e.selections.append(sel);
+ }
+ }
+
+
e.revision = ed->document()->revision();
e.textEditor = textEditor;
- e.selections = selections;
e.ifdefedOutBlocks = blockRanges;
todo.append(e);
m_todo = todo;
@@ -1189,8 +1200,9 @@ void CppModelManager::updateEditorSelections()
else if (editor->document()->revision() != ed.revision)
continue; // outdated
- editor->setExtraSelections(TextEditor::BaseTextEditorWidget::CodeWarningsSelection,
- ed.selections);
+ if (ed.updateSelections)
+ editor->setExtraSelections(TextEditor::BaseTextEditorWidget::CodeWarningsSelection,
+ ed.selections);
editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks);
}
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index 7cf4b85685..a661d1bf41 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -35,7 +35,7 @@
#include "cpptools_global.h"
#include "cpptoolsconstants.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include "ModelManagerInterface.h"
#ifndef ICHECK_BUILD
# include <projectexplorer/project.h>
#endif
@@ -225,8 +225,11 @@ private:
struct Editor {
Editor()
- : revision(-1) {}
+ : revision(-1)
+ , updateSelections(true)
+ {}
int revision;
+ bool updateSelections;
QPointer<TextEditor::ITextEditor> textEditor;
QList<QTextEdit::ExtraSelection> selections;
QList<TextEditor::BaseTextEditorWidget::BlockRange> ifdefedOutBlocks;
@@ -241,6 +244,7 @@ private:
CppFindReferences *m_findReferences;
bool m_indexerEnabled;
+ bool m_dumpFileNameWhileParsing;
mutable QMutex protectExtraDiagnostics;
QHash<QString, QHash<int, QList<CPlusPlus::Document::DiagnosticMessage> > > m_extraDiagnostics;
@@ -258,7 +262,7 @@ class CPPTOOLS_EXPORT CppPreprocessor: public CPlusPlus::Client
{
public:
#ifndef ICHECK_BUILD
- CppPreprocessor(QPointer<CppModelManager> modelManager);
+ CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing = false);
#else
CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager);
#endif
@@ -300,7 +304,6 @@ protected:
virtual void startExpandingMacro(unsigned offset,
const CPlusPlus::Macro &macro,
const QByteArray &originalText,
- bool inCondition,
const QVector<CPlusPlus::MacroArgumentReference> &actuals);
virtual void stopExpandingMacro(unsigned offset, const CPlusPlus::Macro &macro);
virtual void startSkippingBlocks(unsigned offset);
@@ -312,6 +315,7 @@ private:
#ifndef ICHECK_BUILD
QPointer<CppModelManager> m_modelManager;
#endif
+ bool m_dumpFileNameWhileParsing;
CPlusPlus::Environment env;
CPlusPlus::Preprocessor preprocess;
QStringList m_includePaths;
diff --git a/src/plugins/cpptools/cpprefactoringchanges.h b/src/plugins/cpptools/cpprefactoringchanges.h
index e5612a9a2c..b59a7d48ed 100644
--- a/src/plugins/cpptools/cpprefactoringchanges.h
+++ b/src/plugins/cpptools/cpprefactoringchanges.h
@@ -34,7 +34,7 @@
#define CPPREFACTORINGCHANGES_H
#include <ASTfwd.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include "ModelManagerInterface.h"
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro
index c53c6e8a71..d0a78e7676 100644
--- a/src/plugins/cpptools/cpptools.pro
+++ b/src/plugins/cpptools/cpptools.pro
@@ -45,7 +45,9 @@ HEADERS += completionsettingspage.h \
cppchecksymbols.h \
cpplocalsymbols.h \
cppsemanticinfo.h \
- cppcompletionassistprovider.h
+ cppcompletionassistprovider.h \
+ ModelManagerInterface.h \
+ TypeHierarchyBuilder.h
SOURCES += completionsettingspage.cpp \
cppclassesfilter.cpp \
@@ -82,7 +84,9 @@ SOURCES += completionsettingspage.cpp \
cppchecksymbols.cpp \
cpplocalsymbols.cpp \
cppsemanticinfo.cpp \
- cppcompletionassistprovider.cpp
+ cppcompletionassistprovider.cpp \
+ ModelManagerInterface.cpp \
+ TypeHierarchyBuilder.cpp
FORMS += completionsettingspage.ui \
cppfilesettingspage.ui \
diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs
index 34bd225eae..ad132ef756 100644
--- a/src/plugins/cpptools/cpptools.qbs
+++ b/src/plugins/cpptools/cpptools.qbs
@@ -100,7 +100,15 @@ QtcPlugin {
"uicodecompletionsupport.h",
"cppcompletionassistprovider.cpp",
"cppcompletionassistprovider.h",
- "cppcodestylepreferencesfactory.h"
+ "cppcodestylepreferencesfactory.h",
+ "ModelManagerInterface.cpp",
+ "ModelManagerInterface.h",
+ "TypeHierarchyBuilder.cpp",
+ "TypeHierarchyBuilder.h"
]
+
+ ProductModule {
+ Depends { name: "CPlusPlus" }
+ }
}
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 51319ffc96..b4b167b9a1 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -63,6 +63,7 @@ struct CppFileSettings;
class CppToolsPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CppTools.json")
public:
CppToolsPlugin();
diff --git a/src/plugins/cpptools/uicodecompletionsupport.h b/src/plugins/cpptools/uicodecompletionsupport.h
index ad0495942c..acd8f5f81e 100644
--- a/src/plugins/cpptools/uicodecompletionsupport.h
+++ b/src/plugins/cpptools/uicodecompletionsupport.h
@@ -37,7 +37,7 @@
#include "cpptools_global.h"
#include "abstracteditorsupport.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include "ModelManagerInterface.h"
#include <QDateTime>
diff --git a/src/plugins/cvs/cvsplugin.h b/src/plugins/cvs/cvsplugin.h
index f95caff99c..6f077e61a1 100644
--- a/src/plugins/cvs/cvsplugin.h
+++ b/src/plugins/cvs/cvsplugin.h
@@ -74,6 +74,7 @@ struct CvsResponse
class CvsPlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CVS.json")
public:
CvsPlugin();
diff --git a/src/plugins/debugger/Debugger.pluginspec.in b/src/plugins/debugger/Debugger.pluginspec.in
index 1b4327cc0a..77d13834f8 100644
--- a/src/plugins/debugger/Debugger.pluginspec.in
+++ b/src/plugins/debugger/Debugger.pluginspec.in
@@ -17,6 +17,7 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
<dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"Find\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
<!-- Debugger plugin adds items to the editor\'s context menu -->
<dependency name=\"CppEditor\" version=\"$$QTCREATOR_VERSION\" type=\"optional\"/>
</dependencyList>
diff --git a/src/plugins/debugger/basewindow.cpp b/src/plugins/debugger/basewindow.cpp
index 133dc86be0..92fdcd0d91 100644
--- a/src/plugins/debugger/basewindow.cpp
+++ b/src/plugins/debugger/basewindow.cpp
@@ -35,116 +35,44 @@
#include "debuggeractions.h"
#include "debuggercore.h"
+#include <aggregation/aggregate.h>
+#include <coreplugin/findplaceholder.h>
+#include <find/treeviewfind.h>
#include <utils/savedaction.h>
-#include <QDebug>
-#include <QContextMenuEvent>
-#include <QHeaderView>
#include <QMenu>
+#include <QVBoxLayout>
namespace Debugger {
namespace Internal {
-BaseWindow::BaseWindow(QWidget *parent)
- : QTreeView(parent)
+BaseTreeView::BaseTreeView(QWidget *parent)
+ : Utils::BaseTreeView(parent)
{
QAction *act = debuggerCore()->action(UseAlternatingRowColors);
-
- setAttribute(Qt::WA_MacShowFocusRect, false);
- setFrameStyle(QFrame::NoFrame);
setAlternatingRowColors(act->isChecked());
- setRootIsDecorated(false);
- setIconSize(QSize(10, 10));
- setSelectionMode(QAbstractItemView::ExtendedSelection);
- setUniformRowHeights(true);
-
- header()->setDefaultAlignment(Qt::AlignLeft);
- header()->setClickable(true);
-
connect(act, SIGNAL(toggled(bool)),
- SLOT(setAlternatingRowColorsHelper(bool)));
- connect(this, SIGNAL(activated(QModelIndex)),
- SLOT(rowActivatedHelper(QModelIndex)));
- connect(header(), SIGNAL(sectionClicked(int)),
- SLOT(headerSectionClicked(int)));
-
- m_adjustColumnsAction = new QAction(tr("Adjust Column Widths to Contents"), 0);
- m_alwaysAdjustColumnsAction = 0;
-}
-
-void BaseWindow::setAlwaysAdjustColumnsAction(QAction *action)
-{
- m_alwaysAdjustColumnsAction = action;
- connect(action, SIGNAL(toggled(bool)),
- SLOT(setAlwaysResizeColumnsToContents(bool)));
+ SLOT(setAlternatingRowColorsHelper(bool)));
}
-void BaseWindow::addBaseContextActions(QMenu *menu)
+void BaseTreeView::addBaseContextActions(QMenu *menu)
{
- menu->addSeparator();
- if (m_alwaysAdjustColumnsAction)
- menu->addAction(m_alwaysAdjustColumnsAction);
- menu->addAction(m_adjustColumnsAction);
- menu->addSeparator();
+ Utils::BaseTreeView::addBaseContextActions(menu);
menu->addAction(debuggerCore()->action(SettingsDialog));
}
-bool BaseWindow::handleBaseContextAction(QAction *act)
-{
- if (act == 0)
- return true;
- if (act == m_adjustColumnsAction) {
- resizeColumnsToContents();
- return true;
- }
- if (act == m_alwaysAdjustColumnsAction) {
- if (act->isChecked())
- resizeColumnsToContents();
- // Action triggered automatically.
- return true;
- }
- return false;
-}
-
-void BaseWindow::setModel(QAbstractItemModel *model)
-{
- QTreeView::setModel(model);
- if (header() && m_alwaysAdjustColumnsAction)
- setAlwaysResizeColumnsToContents(m_alwaysAdjustColumnsAction->isChecked());
-}
-
-void BaseWindow::mousePressEvent(QMouseEvent *ev)
-{
- QTreeView::mousePressEvent(ev);
- if (!indexAt(ev->pos()).isValid())
- resizeColumnsToContents();
-}
-
-void BaseWindow::resizeColumnsToContents()
-{
- const int columnCount = model()->columnCount();
- for (int c = 0 ; c != columnCount; ++c)
- resizeColumnToContents(c);
-}
-
-void BaseWindow::setAlwaysResizeColumnsToContents(bool on)
-{
- QHeaderView::ResizeMode mode = on
- ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
- header()->setResizeMode(0, mode);
-}
-
-void BaseWindow::headerSectionClicked(int logicalIndex)
-{
- resizeColumnToContents(logicalIndex);
-}
-
-void BaseWindow::reset()
+BaseWindow::BaseWindow(QTreeView *treeView, QWidget *parent)
+ : QWidget(parent), m_treeView(treeView)
{
- QTreeView::reset();
- if (header() && m_alwaysAdjustColumnsAction
- && m_alwaysAdjustColumnsAction->isChecked())
- resizeColumnsToContents();
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ vbox->setMargin(0);
+ vbox->setSpacing(0);
+ vbox->addWidget(m_treeView);
+ vbox->addWidget(new Core::FindToolBarPlaceHolder(this));
+
+ Aggregation::Aggregate *agg = new Aggregation::Aggregate;
+ agg->add(m_treeView);
+ agg->add(new Find::TreeViewFind(m_treeView));
}
} // namespace Internal
diff --git a/src/plugins/debugger/basewindow.h b/src/plugins/debugger/basewindow.h
index 1592df4202..4a0b95d8b8 100644
--- a/src/plugins/debugger/basewindow.h
+++ b/src/plugins/debugger/basewindow.h
@@ -33,39 +33,30 @@
#ifndef DEBUGGER_BASEWINDOW_H
#define DEBUGGER_BASEWINDOW_H
-#include <QTreeView>
+#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
-class BaseWindow : public QTreeView
+class BaseTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
- BaseWindow(QWidget *parent = 0);
-
- void setAlwaysAdjustColumnsAction(QAction *action);
+ explicit BaseTreeView(QWidget *parent = 0);
void addBaseContextActions(QMenu *menu);
- bool handleBaseContextAction(QAction *action);
-
- void setModel(QAbstractItemModel *model);
- virtual void rowActivated(const QModelIndex &) {}
- void mousePressEvent(QMouseEvent *ev);
-
-public slots:
- void resizeColumnsToContents();
- void setAlwaysResizeColumnsToContents(bool on);
+};
-private slots:
- void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
- void rowActivatedHelper(const QModelIndex &index) { rowActivated(index); }
- void headerSectionClicked(int logicalIndex);
- void reset();
+class BaseWindow : public QWidget
+{
+public:
+ explicit BaseWindow(QTreeView *treeView, QWidget *parent = 0);
+ void setModel(QAbstractItemModel *model) { m_treeView->setModel(model); }
+ QHeaderView *header() const { return m_treeView->header(); }
+ QAbstractItemModel *model() const { return m_treeView->model(); }
private:
- QAction *m_alwaysAdjustColumnsAction;
- QAction *m_adjustColumnsAction;
+ QTreeView *m_treeView;
};
} // namespace Internal
diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp
index 699025a18e..63856af81f 100644
--- a/src/plugins/debugger/breakwindow.cpp
+++ b/src/plugins/debugger/breakwindow.cpp
@@ -486,11 +486,9 @@ MultiBreakPointsDialog::MultiBreakPointsDialog(QWidget *parent) :
//
///////////////////////////////////////////////////////////////////////
-BreakWindow::BreakWindow(QWidget *parent)
- : BaseWindow(parent)
+BreakTreeView::BreakTreeView(QWidget *parent)
+ : BaseTreeView(parent)
{
- setWindowTitle(tr("Breakpoints"));
- setObjectName(QLatin1String("ThreadsWindow"));
setWindowIcon(QIcon(QLatin1String(":/debugger/images/debugger_breakpoints.png")));
setSelectionMode(QAbstractItemView::ExtendedSelection);
setAlwaysAdjustColumnsAction(debuggerCore()->action(AlwaysAdjustBreakpointsColumnWidths));
@@ -498,12 +496,12 @@ BreakWindow::BreakWindow(QWidget *parent)
SIGNAL(toggled(bool)), SLOT(showAddressColumn(bool)));
}
-void BreakWindow::showAddressColumn(bool on)
+void BreakTreeView::showAddressColumn(bool on)
{
setColumnHidden(7, !on);
}
-void BreakWindow::keyPressEvent(QKeyEvent *ev)
+void BreakTreeView::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete) {
QItemSelectionModel *sm = selectionModel();
@@ -519,7 +517,7 @@ void BreakWindow::keyPressEvent(QKeyEvent *ev)
QTreeView::keyPressEvent(ev);
}
-void BreakWindow::mouseDoubleClickEvent(QMouseEvent *ev)
+void BreakTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
{
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (indexUnderMouse.isValid() && indexUnderMouse.column() >= 4) {
@@ -529,16 +527,16 @@ void BreakWindow::mouseDoubleClickEvent(QMouseEvent *ev)
QTreeView::mouseDoubleClickEvent(ev);
}
-void BreakWindow::setModel(QAbstractItemModel *model)
+void BreakTreeView::setModel(QAbstractItemModel *model)
{
- BaseWindow::setModel(model);
+ BaseTreeView::setModel(model);
resizeColumnToContents(0); // Number
resizeColumnToContents(3); // Line
resizeColumnToContents(6); // Ignore count
connect(model, SIGNAL(layoutChanged()), this, SLOT(expandAll()));
}
-void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
+void BreakTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
QItemSelectionModel *sm = selectionModel();
@@ -654,21 +652,21 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
-void BreakWindow::setBreakpointsEnabled(const BreakpointModelIds &ids, bool enabled)
+void BreakTreeView::setBreakpointsEnabled(const BreakpointModelIds &ids, bool enabled)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointModelId id, ids)
handler->setEnabled(id, enabled);
}
-void BreakWindow::deleteBreakpoints(const BreakpointModelIds &ids)
+void BreakTreeView::deleteBreakpoints(const BreakpointModelIds &ids)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointModelId id, ids)
handler->removeBreakpoint(id);
}
-void BreakWindow::editBreakpoint(BreakpointModelId id, QWidget *parent)
+void BreakTreeView::editBreakpoint(BreakpointModelId id, QWidget *parent)
{
BreakpointParameters data = breakHandler()->breakpointData(id);
BreakpointParts parts = NoParts;
@@ -677,7 +675,7 @@ void BreakWindow::editBreakpoint(BreakpointModelId id, QWidget *parent)
breakHandler()->changeBreakpointData(id, data, parts);
}
-void BreakWindow::addBreakpoint()
+void BreakTreeView::addBreakpoint()
{
BreakpointParameters data(BreakpointByFileAndLine);
BreakpointParts parts = NoParts;
@@ -687,7 +685,7 @@ void BreakWindow::addBreakpoint()
breakHandler()->appendBreakpoint(data);
}
-void BreakWindow::editBreakpoints(const BreakpointModelIds &ids)
+void BreakTreeView::editBreakpoints(const BreakpointModelIds &ids)
{
QTC_ASSERT(!ids.isEmpty(), return);
@@ -726,18 +724,24 @@ void BreakWindow::editBreakpoints(const BreakpointModelIds &ids)
}
}
-void BreakWindow::associateBreakpoint(const BreakpointModelIds &ids, int threadId)
+void BreakTreeView::associateBreakpoint(const BreakpointModelIds &ids, int threadId)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointModelId id, ids)
handler->setThreadSpec(id, threadId);
}
-void BreakWindow::rowActivated(const QModelIndex &index)
+void BreakTreeView::rowActivated(const QModelIndex &index)
{
breakHandler()->gotoLocation(breakHandler()->findBreakpointByIndex(index));
}
+BreakWindow::BreakWindow()
+ : BaseWindow(new BreakTreeView)
+{
+ setWindowTitle(tr("Breakpoints"));
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/breakwindow.h b/src/plugins/debugger/breakwindow.h
index 877f3dfcaa..b06055ad10 100644
--- a/src/plugins/debugger/breakwindow.h
+++ b/src/plugins/debugger/breakwindow.h
@@ -39,12 +39,12 @@
namespace Debugger {
namespace Internal {
-class BreakWindow : public BaseWindow
+class BreakTreeView : public BaseTreeView
{
Q_OBJECT
public:
- explicit BreakWindow(QWidget *parent = 0);
+ explicit BreakTreeView(QWidget *parent = 0);
static void editBreakpoint(BreakpointModelId id, QWidget *parent);
void setModel(QAbstractItemModel *model);
@@ -65,6 +65,12 @@ private:
void setBreakpointsEnabled(const BreakpointModelIds &ids, bool enabled);
};
+class BreakWindow : public BaseWindow
+{
+public:
+ BreakWindow();
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index eac6068952..bce3ac1910 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -75,7 +75,7 @@
#include <cplusplus/findcdbbreakpoint.h>
#include <cplusplus/CppDocument.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <QCoreApplication>
#include <QFileInfo>
diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp
index 4718a1cc9f..98806c9784 100644
--- a/src/plugins/debugger/commonoptionspage.cpp
+++ b/src/plugins/debugger/commonoptionspage.cpp
@@ -72,6 +72,8 @@ CommonOptionsPageWidget::CommonOptionsPageWidget
m_ui.checkBoxCloseBuffersOnExit);
m_group->insert(dc->action(SwitchModeOnExit),
m_ui.checkBoxSwitchModeOnExit);
+ m_group->insert(dc->action(RaiseOnInterrupt),
+ m_ui.checkBoxBringToForegroundOnInterrrupt);
m_group->insert(dc->action(FontSizeFollowsEditor),
m_ui.checkBoxFontSizeFollowsEditor);
m_group->insert(dc->action(AutoDerefPointers), 0);
@@ -120,6 +122,7 @@ QString CommonOptionsPageWidget::searchKeyWords() const
<< sep << m_ui.checkBoxCloseBuffersOnExit->text()
<< sep << m_ui.checkBoxSwitchModeOnExit->text()
<< sep << m_ui.labelMaximalStackDepth->text()
+ << sep << m_ui.checkBoxBringToForegroundOnInterrrupt->text()
;
rc.remove(QLatin1Char('&'));
return rc;
diff --git a/src/plugins/debugger/commonoptionspage.ui b/src/plugins/debugger/commonoptionspage.ui
index 8cf9ae4f0e..b44f2f36cb 100644
--- a/src/plugins/debugger/commonoptionspage.ui
+++ b/src/plugins/debugger/commonoptionspage.ui
@@ -65,16 +65,6 @@
</property>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="checkBoxRegisterForPostMortem">
- <property name="toolTip">
- <string>Register Qt Creator for debugging crashed applications.</string>
- </property>
- <property name="text">
- <string>Use Qt Creator for post-mortem debugging</string>
- </property>
- </widget>
- </item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -130,6 +120,23 @@
</item>
</layout>
</item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="checkBoxRegisterForPostMortem">
+ <property name="toolTip">
+ <string>Register Qt Creator for debugging crashed applications.</string>
+ </property>
+ <property name="text">
+ <string>Use Qt Creator for post-mortem debugging</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="checkBoxBringToForegroundOnInterrrupt">
+ <property name="text">
+ <string>Bring Qt Creator to foreground when application interrupts</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp
index 0712a37124..ffb0d1842d 100644
--- a/src/plugins/debugger/debuggeractions.cpp
+++ b/src/plugins/debugger/debuggeractions.cpp
@@ -398,6 +398,12 @@ DebuggerSettings::DebuggerSettings(QSettings *settings)
insertItem(SwitchModeOnExit, item);
item = new SavedAction(this);
+ item->setSettingsKey(debugModeGroup, QLatin1String("RaiseOnInterrupt"));
+ item->setCheckable(true);
+ item->setDefaultValue(true);
+ insertItem(RaiseOnInterrupt, item);
+
+ item = new SavedAction(this);
item->setSettingsKey(debugModeGroup, QLatin1String("AutoQuit"));
item->setText(tr("Automatically Quit Debugger"));
item->setCheckable(true);
diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h
index 1bdc34c496..9da4ab1b05 100644
--- a/src/plugins/debugger/debuggeractions.h
+++ b/src/plugins/debugger/debuggeractions.h
@@ -100,6 +100,7 @@ enum DebuggerActionCode
OperateByInstruction,
CloseBuffersOnExit,
SwitchModeOnExit,
+ RaiseOnInterrupt,
UseDebuggingHelpers,
diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h
index 1367cbeb5b..5925f2df17 100644
--- a/src/plugins/debugger/debuggerconstants.h
+++ b/src/plugins/debugger/debuggerconstants.h
@@ -134,6 +134,12 @@ enum DebuggerStartMode
StartRemoteEngine // Start ipc guest engine on other machine
};
+enum DebuggerCloseMode
+{
+ KillAtClose,
+ DetachAtClose
+};
+
enum DebuggerCapabilities
{
ReverseSteppingCapability = 0x1,
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index ab623dee5b..1a53d207c6 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -61,8 +61,8 @@
#include <texteditor/itexteditor.h>
#include <texteditor/basetextmark.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/taskhub.h>
-#include <extensionsystem/pluginmanager.h>
#include <utils/savedaction.h>
#include <utils/qtcassert.h>
@@ -269,6 +269,8 @@ public slots:
m_disassemblerAgent.resetLocation();
}
+ TaskHub *taskHub();
+
public:
DebuggerState state() const { return m_state; }
RemoteSetupState remoteSetupState() const { return m_remoteSetupState; }
@@ -1038,6 +1040,8 @@ void DebuggerEngine::notifyInferiorSpontaneousStop()
QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state());
showStatusMessage(tr("Stopped."));
setState(InferiorStopOk);
+ if (debuggerCore()->boolSetting(RaiseOnInterrupt))
+ emit raiseWindow();
}
void DebuggerEngine::notifyInferiorStopFailed()
@@ -1394,6 +1398,8 @@ void DebuggerEngine::quitDebugger()
break;
case EngineRunFailed:
case DebuggerFinished:
+ case InferiorExitOk:
+ case InferiorShutdownOk:
break;
case InferiorSetupRequested:
notifyInferiorSetupFailed();
@@ -1518,6 +1524,7 @@ void DebuggerEngine::updateAll()
void DebuggerEngine::attemptBreakpointSynchronization()
{
+ showMessage(_("ATTEMPT BREAKPOINT SYNCHRONIZATION"));
if (!stateAcceptsBreakpointChanges()) {
showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
return;
@@ -1527,8 +1534,14 @@ void DebuggerEngine::attemptBreakpointSynchronization()
foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
// Take ownership of the breakpoint. Requests insertion.
- if (acceptsBreakpoint(id))
+ if (acceptsBreakpoint(id)) {
+ showMessage(_("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2")
+ .arg(id.toString()).arg(handler->state(id)));
handler->setEngine(id, this);
+ } else {
+ showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE")
+ .arg(id.toString()).arg(handler->state(id)));
+ }
}
bool done = true;
@@ -1568,8 +1581,12 @@ void DebuggerEngine::attemptBreakpointSynchronization()
QTC_ASSERT(false, qDebug() << "UNKNOWN STATE" << id << state());
}
- if (done)
+ if (done) {
+ showMessage(_("BREAKPOINTS ARE SYNCHRONIZED"));
d->m_disassemblerAgent.updateBreakpointMarkers();
+ } else {
+ showMessage(_("BREAKPOINTS ARE NOT FULLY SYNCHRONIZED"));
+ }
}
void DebuggerEngine::insertBreakpoint(BreakpointModelId id)
@@ -1951,15 +1968,23 @@ void DebuggerEnginePrivate::reportTestError(const QString &msg, int line)
{
m_engine->showMessage(_("### Line %1: %2").arg(line).arg(msg));
m_foundError = true;
+ Task task(Task::Error, msg, Utils::FileName::fromUserInput(m_testFileName), line + 1, Core::Id("DebuggerTest"));
+ taskHub()->addTask(task);
+}
+TaskHub *DebuggerEnginePrivate::taskHub()
+{
if (!m_taskHub) {
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
- m_taskHub = pm->getObject<TaskHub>();
+ m_taskHub = ProjectExplorerPlugin::instance()->taskHub();
+ m_taskHub->addCategory(Core::Id("Debuginfo"), tr("Debug Information"));
m_taskHub->addCategory(Core::Id("DebuggerTest"), tr("Debugger Test"));
}
+ return m_taskHub;
+}
- Task task(Task::Error, msg, Utils::FileName::fromUserInput(m_testFileName), line + 1, Core::Id("DebuggerTest"));
- m_taskHub->addTask(task);
+TaskHub *DebuggerEngine::taskHub()
+{
+ return d->taskHub();
}
} // namespace Debugger
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index f2f133dff1..8665edd954 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -47,13 +47,9 @@ class QMessageBox;
class QAbstractItemModel;
QT_END_NAMESPACE
-namespace TextEditor {
-class ITextEditor;
-}
-
-namespace Core {
-class IOptionsPage;
-}
+namespace TextEditor { class ITextEditor; }
+namespace Core { class IOptionsPage; }
+namespace ProjectExplorer { class TaskHub; }
namespace Debugger {
@@ -201,6 +197,7 @@ public:
virtual void createSnapshot();
virtual void updateAll();
+ ProjectExplorer::TaskHub *taskHub();
typedef Internal::BreakpointModelId BreakpointModelId;
virtual bool stateAcceptsBreakpointChanges() const { return true; }
@@ -289,10 +286,9 @@ public:
QString toFileInProject(const QUrl &fileUrl);
signals:
- void stateChanged(const Debugger::DebuggerState &state);
+ void stateChanged(Debugger::DebuggerState state);
// A new stack frame is on display including locals.
void stackFrameCompleted();
- void updateViewsRequested();
/*
* For "external" clients of a debugger run control that needs to do
* further setup before the debugger is started (e.g. Maemo).
@@ -302,6 +298,7 @@ signals:
* a server start script should be used, but none is given.
*/
void requestRemoteSetup();
+ void raiseWindow();
protected:
// The base notify*() function implementation should be sufficient
diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp
index d5c1102b9e..bce0213b5f 100644
--- a/src/plugins/debugger/debuggermainwindow.cpp
+++ b/src/plugins/debugger/debuggermainwindow.cpp
@@ -32,7 +32,9 @@
#include "debuggermainwindow.h"
#include "debuggercore.h"
+#include "debuggerengine.h"
+#include <utils/appmainwindow.h>
#include <utils/styledbar.h>
#include <utils/qtcassert.h>
#include <utils/fancymainwindow.h>
@@ -124,7 +126,8 @@ public:
DebuggerLanguages m_supportedLanguages;
- QStackedWidget *m_toolBarStack;
+ QWidget *m_debugToolBar;
+ QHBoxLayout *m_debugToolBarLayout;
QHash<DebuggerLanguage, Context> m_contextsForLanguage;
@@ -141,13 +144,16 @@ public:
Project *m_previousProject;
Target *m_previousTarget;
RunConfiguration *m_previousRunConfiguration;
+
+ DebuggerEngine *m_engine;
};
DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *mw)
: q(mw)
, m_resizeEventFilter(this)
, m_supportedLanguages(AnyLanguage)
- , m_toolBarStack(new QStackedWidget)
+ , m_debugToolBar(new QWidget)
+ , m_debugToolBarLayout(new QHBoxLayout(m_debugToolBar))
, m_inDebugMode(false)
, m_changingUI(false)
, m_previousDebugLanguages(AnyLanguage)
@@ -157,8 +163,12 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *mw)
, m_previousProject(0)
, m_previousTarget(0)
, m_previousRunConfiguration(0)
+ , m_engine(0)
{
+ m_debugToolBarLayout->setMargin(0);
+ m_debugToolBarLayout->setSpacing(0);
createViewsMenuItems();
+ addLanguage(AnyLanguage, Context());
addLanguage(CppLanguage, Context(C_CPPDEBUGGER));
addLanguage(QmlLanguage, Context(C_QMLDEBUGGER));
}
@@ -271,6 +281,15 @@ DebuggerMainWindow::~DebuggerMainWindow()
delete d;
}
+void DebuggerMainWindow::setCurrentEngine(DebuggerEngine *engine)
+{
+ if (d->m_engine)
+ disconnect(d->m_engine, SIGNAL(raiseWindow()), this, SLOT(raiseDebuggerWindow()));
+ d->m_engine = engine;
+ if (d->m_engine)
+ connect(d->m_engine, SIGNAL(raiseWindow()), this, SLOT(raiseDebuggerWindow()));
+}
+
DebuggerLanguages DebuggerMainWindow::activeDebugLanguages() const
{
return d->m_activeDebugLanguages;
@@ -353,8 +372,10 @@ void DebuggerMainWindowPrivate::activateQmlCppLayout()
{
Context qmlCppContext = m_contextsForLanguage.value(QmlLanguage);
qmlCppContext.add(m_contextsForLanguage.value(CppLanguage));
- if (m_toolBars.value(QmlLanguage))
- m_toolBarStack->setCurrentWidget(m_toolBars.value(QmlLanguage));
+ if (m_toolBars.value(QmlLanguage)) {
+ m_toolBars.value(QmlLanguage)->show();
+ m_debugToolBarLayout->insertWidget(1, m_toolBars.value(QmlLanguage));
+ }
if (m_previousDebugLanguages & QmlLanguage) {
m_dockWidgetActiveStateQmlCpp = q->saveSettings();
@@ -373,7 +394,10 @@ void DebuggerMainWindowPrivate::activateCppLayout()
{
Context qmlCppContext = m_contextsForLanguage.value(QmlLanguage);
qmlCppContext.add(m_contextsForLanguage.value(CppLanguage));
- m_toolBarStack->setCurrentWidget(m_toolBars.value(CppLanguage));
+ if (m_toolBars.value(QmlLanguage)) {
+ m_toolBars.value(QmlLanguage)->hide();
+ m_debugToolBarLayout->removeWidget(m_toolBars.value(QmlLanguage));
+ }
if (m_previousDebugLanguages & QmlLanguage) {
m_dockWidgetActiveStateQmlCpp = q->saveSettings();
@@ -394,7 +418,12 @@ void DebuggerMainWindow::setToolBar(DebuggerLanguage language, QWidget *widget)
{
Q_ASSERT(d->m_toolBars.contains(language));
d->m_toolBars[language] = widget;
- d->m_toolBarStack->addWidget(widget);
+ if (language == CppLanguage)
+ d->m_debugToolBarLayout->addWidget(widget);
+
+ //Add widget at the end
+ if (language == AnyLanguage)
+ d->m_debugToolBarLayout->insertWidget(-1, widget, 10);
}
QDockWidget *DebuggerMainWindow::dockWidget(const QString &objectName) const
@@ -493,7 +522,7 @@ QWidget *DebuggerMainWindow::createContents(IMode *mode)
QHBoxLayout *debugToolBarLayout = new QHBoxLayout(debugToolBar);
debugToolBarLayout->setMargin(0);
debugToolBarLayout->setSpacing(0);
- debugToolBarLayout->addWidget(d->m_toolBarStack);
+ debugToolBarLayout->addWidget(d->m_debugToolBar);
debugToolBarLayout->addWidget(new Utils::StyledSeparator);
QDockWidget *dock = new QDockWidget(DebuggerMainWindowPrivate::tr("Debugger Toolbar"));
@@ -559,6 +588,14 @@ void DebuggerMainWindow::writeSettings() const
settings->endGroup();
}
+void DebuggerMainWindow::raiseDebuggerWindow()
+{
+ Core::ICore *core = Core::ICore::instance();
+ Utils::AppMainWindow *appMainWindow = qobject_cast<Utils::AppMainWindow*>(core->mainWindow());
+ QTC_ASSERT(appMainWindow, return)
+ appMainWindow->raiseWindow();
+}
+
void DebuggerMainWindow::readSettings()
{
QSettings *settings = ICore::settings();
diff --git a/src/plugins/debugger/debuggermainwindow.h b/src/plugins/debugger/debuggermainwindow.h
index a43ee833c5..ed82cbdd72 100644
--- a/src/plugins/debugger/debuggermainwindow.h
+++ b/src/plugins/debugger/debuggermainwindow.h
@@ -45,6 +45,8 @@ class IMode;
namespace Debugger {
+class DebuggerEngine;
+
namespace Internal {
class DebuggerMainWindowPrivate;
}
@@ -57,6 +59,8 @@ public:
DebuggerMainWindow();
~DebuggerMainWindow();
+ void setCurrentEngine(DebuggerEngine *engine);
+
// Debugger toolbars are registered with this function.
void setToolBar(DebuggerLanguage language, QWidget *widget);
@@ -81,6 +85,9 @@ public:
void readSettings();
void writeSettings() const;
+private slots:
+ void raiseDebuggerWindow();
+
signals:
void activeDebugLanguagesChanged(Debugger::DebuggerLanguages);
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 844497c42e..f568d9346c 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -84,7 +84,7 @@
#include <coreplugin/modemanager.h>
#include <cppeditor/cppeditorconstants.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/invoker.h>
@@ -1001,7 +1001,7 @@ public slots:
QTC_ASSERT(act, return);
const BreakpointModelId id = act->data().value<BreakpointModelId>();
QTC_ASSERT(id > 0, return);
- BreakWindow::editBreakpoint(id, mainWindow());
+ BreakTreeView::editBreakpoint(id, mainWindow());
}
void slotRunToLine()
@@ -1169,18 +1169,18 @@ public:
Utils::StatusLabel *m_statusLabel;
QComboBox *m_threadBox;
- BreakWindow *m_breakWindow;
+ BaseWindow *m_breakWindow;
BreakHandler *m_breakHandler;
QtMessageLogWindow *m_qtMessageLogWindow;
- QTreeView *m_returnWindow;
- QTreeView *m_localsWindow;
- QTreeView *m_watchersWindow;
- QAbstractItemView *m_registerWindow;
- QAbstractItemView *m_modulesWindow;
- QAbstractItemView *m_snapshotWindow;
- SourceFilesWindow *m_sourceFilesWindow;
- QAbstractItemView *m_stackWindow;
- QAbstractItemView *m_threadsWindow;
+ WatchWindow *m_returnWindow;
+ WatchWindow *m_localsWindow;
+ WatchWindow *m_watchersWindow;
+ BaseWindow *m_registerWindow;
+ BaseWindow *m_modulesWindow;
+ BaseWindow *m_snapshotWindow;
+ BaseWindow *m_sourceFilesWindow;
+ BaseWindow *m_stackWindow;
+ BaseWindow *m_threadsWindow;
LogWindow *m_logWindow;
bool m_busy;
@@ -1339,6 +1339,7 @@ bool DebuggerPluginPrivate::parseArgument(QStringList::const_iterator &it,
qulonglong pid = it->toULongLong();
if (pid) {
sp.startMode = AttachExternal;
+ sp.closeMode = DetachAtClose;
sp.attachPID = pid;
sp.displayName = tr("Process %1").arg(sp.attachPID);
sp.startMessage = tr("Attaching to local process %1.").arg(sp.attachPID);
@@ -1560,6 +1561,7 @@ void DebuggerPluginPrivate::attachExternalApplication()
sp.displayName = tr("Process %1").arg(dlg.attachPID());
sp.executable = dlg.executable();
sp.startMode = AttachExternal;
+ sp.closeMode = DetachAtClose;
sp.toolChainAbi = dlg.abi();
sp.debuggerCommand = dlg.debuggerCommand();
if (DebuggerRunControl *rc = createDebugger(sp))
@@ -1572,6 +1574,7 @@ void DebuggerPluginPrivate::attachExternalApplication(RunControl *rc)
sp.attachPID = rc->applicationProcessHandle().pid();
sp.displayName = tr("Debugger attached to %1").arg(rc->displayName());
sp.startMode = AttachExternal;
+ sp.closeMode = DetachAtClose;
sp.toolChainAbi = rc->abi();
if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc);
@@ -1617,6 +1620,7 @@ void DebuggerPluginPrivate::attachToRemoteServer(const QString &spec)
sp.remoteArchitecture = spec.section(QLatin1Char('@'), 2, 2);
sp.displayName = tr("Remote: \"%1\"").arg(sp.remoteChannel);
sp.startMode = AttachToRemoteServer;
+ sp.closeMode = KillAtClose;
sp.toolChainAbi = anyAbiOfBinary(sp.executable);
if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc);
@@ -1630,6 +1634,7 @@ void DebuggerPluginPrivate::startRemoteCdbSession()
sp.toolChainAbi = Abi(hostAbi.architecture(), Abi::WindowsOS,
Abi::WindowsMsvc2010Flavor, Abi::PEFormat, hostAbi.wordWidth());
sp.startMode = AttachToRemoteServer;
+ sp.closeMode = KillAtClose;
StartRemoteCdbDialog dlg(mainWindow());
QString previousConnection = configValue(connectionKey).toString();
if (previousConnection.isEmpty())
@@ -1666,6 +1671,7 @@ void DebuggerPluginPrivate::attachToRemoteServer()
DebuggerStartParameters sp;
if (StartRemoteDialog::run(mainWindow(), m_coreSettings, false, &sp)) {
sp.startMode = AttachToRemoteServer;
+ sp.closeMode = KillAtClose;
sp.useServerStartScript = false;
sp.serverStartScript.clear();
if (RunControl *rc = createDebugger(sp))
@@ -1742,6 +1748,7 @@ void DebuggerPluginPrivate::attachedToProcess(const QString &channel,
sp.sysroot = sysroot;
sp.executable = localExecutable;
sp.startMode = AttachToRemoteServer;
+ sp.closeMode = KillAtClose;
sp.overrideStartScript.clear();
sp.useServerStartScript = false;
sp.serverStartScript.clear();
@@ -1785,6 +1792,7 @@ void DebuggerPluginPrivate::attachToQmlPort()
sp.sysroot = dlg.sysroot();
sp.startMode = AttachToRemoteServer;
+ sp.closeMode = KillAtClose;
sp.languages = QmlLanguage;
//
@@ -2114,6 +2122,7 @@ void DebuggerPluginPrivate::connectEngine(DebuggerEngine *engine)
engine->watchHandler()->rebuildModel();
mainWindow()->setEngineDebugLanguages(engine->languages());
+ mainWindow()->setCurrentEngine(engine);
}
static void changeFontSize(QWidget *widget, qreal size)
@@ -2332,6 +2341,8 @@ void DebuggerPluginPrivate::updateState(DebuggerEngine *engine)
m_attachToRemoteServerAction->setEnabled(true);
m_attachToRemoteProcessAction->setEnabled(true);
+ m_threadBox->setEnabled(state == InferiorStopOk);
+
const bool isCore = engine->startParameters().startMode == AttachCore;
const bool stopped = state == InferiorStopOk;
const bool detachable = stopped && !isCore;
@@ -2897,11 +2908,11 @@ void DebuggerPluginPrivate::extensionsInitialized()
m_sourceFilesWindow->setObjectName(QLatin1String(DOCKWIDGET_SOURCE_FILES));
m_threadsWindow = new ThreadsWindow;
m_threadsWindow->setObjectName(QLatin1String(DOCKWIDGET_THREADS));
- m_returnWindow = new WatchWindow(WatchWindow::ReturnType);
+ m_returnWindow = new WatchWindow(WatchTreeView::ReturnType);
m_returnWindow->setObjectName(QLatin1String("CppDebugReturn"));
- m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
+ m_localsWindow = new WatchWindow(WatchTreeView::LocalsType);
m_localsWindow->setObjectName(QLatin1String("CppDebugLocals"));
- m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
+ m_watchersWindow = new WatchWindow(WatchTreeView::WatchersType);
m_watchersWindow->setObjectName(QLatin1String("CppDebugWatchers"));
// Snapshot
@@ -3446,9 +3457,9 @@ void DebuggerPluginPrivate::extensionsInitialized()
hbox->addWidget(m_threadBox);
hbox->addSpacerItem(new QSpacerItem(4, 0));
- hbox->addWidget(m_statusLabel, 10);
m_mainWindow->setToolBar(CppLanguage, toolbarContainer);
+ m_mainWindow->setToolBar(AnyLanguage, m_statusLabel);
connect(action(EnableReverseDebugging),
SIGNAL(valueChanged(QVariant)),
diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h
index c18f93bbe9..4850459572 100644
--- a/src/plugins/debugger/debuggerplugin.h
+++ b/src/plugins/debugger/debuggerplugin.h
@@ -53,6 +53,7 @@ class DebuggerStartParameters;
class DEBUGGER_EXPORT DebuggerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Debugger.json")
public:
DebuggerPlugin();
diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp
index fcb8fc788e..f4296747e4 100644
--- a/src/plugins/debugger/debuggerrunner.cpp
+++ b/src/plugins/debugger/debuggerrunner.cpp
@@ -167,9 +167,10 @@ public:
QString displayName() const { return tr("Debugger Settings"); }
private slots:
- void useCppDebuggerToggled(bool toggled);
- void useQmlDebuggerToggled(bool toggled);
+ void useCppDebuggerToggled(bool on);
+ void useQmlDebuggerToggled(bool on);
void qmlDebugServerPortChanged(int port);
+ void useMultiProcessToggled(bool on);
public:
DebuggerRunConfigurationAspect *m_aspect; // not owned
@@ -179,6 +180,7 @@ public:
QSpinBox *m_debugServerPort;
QLabel *m_debugServerPortLabel;
QLabel *m_qmlDebuggerInfoLabel;
+ QCheckBox *m_useMultiProcess;
};
DebuggerRunConfigWidget::DebuggerRunConfigWidget(RunConfiguration *runConfiguration)
@@ -204,6 +206,12 @@ DebuggerRunConfigWidget::DebuggerRunConfigWidget(RunConfiguration *runConfigurat
m_debugServerPort->setValue(m_aspect->qmlDebugServerPort());
+ static const QByteArray env = qgetenv("QTC_DEBUGGER_MULTIPROCESS");
+ m_useMultiProcess =
+ new QCheckBox(tr("Enable Debugging of Subprocesses"), this);
+ m_useMultiProcess->setChecked(m_aspect->useMultiProcess());
+ m_useMultiProcess->setVisible(env.toInt());
+
connect(m_qmlDebuggerInfoLabel, SIGNAL(linkActivated(QString)),
Core::HelpManager::instance(), SLOT(handleHelpRequest(QString)));
connect(m_useQmlDebugger, SIGNAL(toggled(bool)),
@@ -212,6 +220,8 @@ DebuggerRunConfigWidget::DebuggerRunConfigWidget(RunConfiguration *runConfigurat
SLOT(useCppDebuggerToggled(bool)));
connect(m_debugServerPort, SIGNAL(valueChanged(int)),
SLOT(qmlDebugServerPortChanged(int)));
+ connect(m_useMultiProcess, SIGNAL(toggled(bool)),
+ SLOT(useMultiProcessToggled(bool)));
if (m_aspect->isDisplaySuppressed())
hide();
@@ -242,6 +252,7 @@ DebuggerRunConfigWidget::DebuggerRunConfigWidget(RunConfiguration *runConfigurat
layout->setMargin(0);
layout->addWidget(m_useCppDebugger);
layout->addLayout(qmlLayout);
+ layout->addWidget(m_useMultiProcess);
setLayout(layout);
}
@@ -250,25 +261,30 @@ void DebuggerRunConfigWidget::qmlDebugServerPortChanged(int port)
m_aspect->m_qmlDebugServerPort = port;
}
-void DebuggerRunConfigWidget::useCppDebuggerToggled(bool toggled)
+void DebuggerRunConfigWidget::useCppDebuggerToggled(bool on)
{
- m_aspect->m_useCppDebugger = toggled;
- if (!toggled && !m_useQmlDebugger->isChecked())
+ m_aspect->m_useCppDebugger = on;
+ if (!on && !m_useQmlDebugger->isChecked())
m_useQmlDebugger->setChecked(true);
}
-void DebuggerRunConfigWidget::useQmlDebuggerToggled(bool toggled)
+void DebuggerRunConfigWidget::useQmlDebuggerToggled(bool on)
{
- m_debugServerPort->setEnabled(toggled);
- m_debugServerPortLabel->setEnabled(toggled);
+ m_debugServerPort->setEnabled(on);
+ m_debugServerPortLabel->setEnabled(on);
- m_aspect->m_useQmlDebugger = toggled
+ m_aspect->m_useQmlDebugger = on
? DebuggerRunConfigurationAspect::EnableQmlDebugger
: DebuggerRunConfigurationAspect::DisableQmlDebugger;
- if (!toggled && !m_useCppDebugger->isChecked())
+ if (!on && !m_useCppDebugger->isChecked())
m_useCppDebugger->setChecked(true);
}
+void DebuggerRunConfigWidget::useMultiProcessToggled(bool on)
+{
+ m_aspect->m_useMultiProcess = on;
+}
+
////////////////////////////////////////////////////////////////////////
//
// DebuggerRunControlPrivate
@@ -900,12 +916,15 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu
}
}
- if (runConfiguration->debuggerAspect()->useCppDebugger())
+ DebuggerRunConfigurationAspect *aspect = runConfiguration->debuggerAspect();
+ sp.multiProcess = aspect->useMultiProcess();
+
+ if (aspect->useCppDebugger())
sp.languages |= CppLanguage;
- if (runConfiguration->debuggerAspect()->useQmlDebugger()) {
+ if (aspect->useQmlDebugger()) {
sp.qmlServerAddress = _("127.0.0.1");
- sp.qmlServerPort = runConfiguration->debuggerAspect()->qmlDebugServerPort();
+ sp.qmlServerPort = aspect->qmlDebugServerPort();
sp.languages |= QmlLanguage;
// Makes sure that all bindings go through the JavaScript engine, so that
diff --git a/src/plugins/debugger/debuggerstartparameters.h b/src/plugins/debugger/debuggerstartparameters.h
index 6c556d222e..513bdea8e1 100644
--- a/src/plugins/debugger/debuggerstartparameters.h
+++ b/src/plugins/debugger/debuggerstartparameters.h
@@ -61,13 +61,14 @@ public:
attachPID(-1),
useTerminal(false),
breakOnMain(false),
+ multiProcess(false),
languages(AnyLanguage),
qmlServerAddress(QLatin1String("127.0.0.1")),
qmlServerPort(ProjectExplorer::Constants::QML_DEFAULT_DEBUG_SERVER_PORT),
useServerStartScript(false),
- connParams(Utils::SshConnectionParameters::NoProxy),
requestRemoteSetup(false),
startMode(NoStartMode),
+ closeMode(KillAtClose),
executableUid(0),
communicationChannel(CommunicationChannelTcpIp),
serverPort(0),
@@ -88,6 +89,7 @@ public:
qint64 attachPID;
bool useTerminal;
bool breakOnMain;
+ bool multiProcess;
DebuggerLanguages languages;
// Used by AttachCrashedExternal.
@@ -126,6 +128,7 @@ public:
QString dumperLibrary;
QStringList dumperLibraryLocations;
DebuggerStartMode startMode;
+ DebuggerCloseMode closeMode;
// For Symbian debugging.
quint32 executableUid;
diff --git a/src/plugins/debugger/gdb/abstractgdbadapter.h b/src/plugins/debugger/gdb/abstractgdbadapter.h
index 05410a9d61..51ed0bf5c5 100644
--- a/src/plugins/debugger/gdb/abstractgdbadapter.h
+++ b/src/plugins/debugger/gdb/abstractgdbadapter.h
@@ -75,7 +75,6 @@ public:
virtual void setupInferior() = 0;
virtual void runEngine() = 0;
virtual void interruptInferior() = 0;
- virtual void shutdownInferior() = 0;
virtual void shutdownAdapter() = 0;
virtual AbstractGdbProcess *gdbProc() = 0;
diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp
index 6e1f91f175..28c722a4e0 100644
--- a/src/plugins/debugger/gdb/attachgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp
@@ -116,11 +116,6 @@ void AttachGdbAdapter::interruptInferior()
interruptLocalInferior(startParameters().attachPID);
}
-void AttachGdbAdapter::shutdownInferior()
-{
- m_engine->defaultInferiorShutdown("detach");
-}
-
void AttachGdbAdapter::shutdownAdapter()
{
m_engine->notifyAdapterShutdownOk();
diff --git a/src/plugins/debugger/gdb/attachgdbadapter.h b/src/plugins/debugger/gdb/attachgdbadapter.h
index 01a6feb9fc..e9a41ac49d 100644
--- a/src/plugins/debugger/gdb/attachgdbadapter.h
+++ b/src/plugins/debugger/gdb/attachgdbadapter.h
@@ -59,7 +59,6 @@ private:
void setupInferior();
void runEngine();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
diff --git a/src/plugins/debugger/gdb/codagdbadapter.cpp b/src/plugins/debugger/gdb/codagdbadapter.cpp
index 1cc5135f5e..1049e4a2f5 100644
--- a/src/plugins/debugger/gdb/codagdbadapter.cpp
+++ b/src/plugins/debugger/gdb/codagdbadapter.cpp
@@ -1237,11 +1237,6 @@ void CodaGdbAdapter::cleanup()
}
}
-void CodaGdbAdapter::shutdownInferior()
-{
- m_engine->defaultInferiorShutdown("kill");
-}
-
void CodaGdbAdapter::shutdownAdapter()
{
if (m_gdbProc.state() == QProcess::Running) {
diff --git a/src/plugins/debugger/gdb/codagdbadapter.h b/src/plugins/debugger/gdb/codagdbadapter.h
index d517cdedaf..56efb451a8 100644
--- a/src/plugins/debugger/gdb/codagdbadapter.h
+++ b/src/plugins/debugger/gdb/codagdbadapter.h
@@ -114,7 +114,6 @@ private:
void setupInferior();
void runEngine();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
void sendRunControlTerminateCommand();
void handleRunControlTerminate(const Coda::CodaCommandResult &);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 6af9fe4049..0b31cef073 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -72,14 +72,18 @@
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
+#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/abi.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/taskhub.h>
+#include <projectexplorer/itaskhandler.h>
#include <texteditor/itexteditor.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
+#include <QDirIterator>
#include <QFileInfo>
#include <QMetaObject>
#include <QTime>
@@ -180,6 +184,52 @@ static QByteArray parsePlainConsoleStream(const GdbResponse &response)
///////////////////////////////////////////////////////////////////////
//
+// Debuginfo Taskhandler
+//
+///////////////////////////////////////////////////////////////////////
+
+class DebugInfoTask
+{
+public:
+ QString command;
+};
+
+class DebugInfoTaskHandler : public ProjectExplorer::ITaskHandler
+{
+public:
+ DebugInfoTaskHandler(GdbEngine *engine)
+ : ITaskHandler(_("Debuginfo")), m_engine(engine)
+ {}
+
+ bool canHandle(const Task &task)
+ {
+ return m_debugInfoTasks.contains(task.taskId);
+ }
+
+ void handle(const Task &task)
+ {
+ m_engine->requestDebugInformation(m_debugInfoTasks.value(task.taskId));
+ }
+
+ void addTask(unsigned id, const DebugInfoTask &task)
+ {
+ m_debugInfoTasks[id] = task;
+ }
+
+ QAction *createAction(QObject *parent = 0)
+ {
+ QAction *action = new QAction(tr("Install &Debug Information"), parent);
+ action->setToolTip(tr("This tries to install missing debug information."));
+ return action;
+ }
+
+private:
+ GdbEngine *m_engine;
+ QHash<unsigned, DebugInfoTask> m_debugInfoTasks;
+};
+
+///////////////////////////////////////////////////////////////////////
+//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////
@@ -217,6 +267,9 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
m_gdbAdapter = createAdapter();
+ m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
+ ExtensionSystem::PluginManager::instance()->addObject(m_debugInfoTaskHandler);
+
m_commandTimer.setSingleShot(true);
connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));
@@ -238,6 +291,10 @@ AbstractGdbProcess *GdbEngine::gdbProc() const
GdbEngine::~GdbEngine()
{
+ ExtensionSystem::PluginManager::instance()->removeObject(m_debugInfoTaskHandler);
+ delete m_debugInfoTaskHandler;
+ m_debugInfoTaskHandler = 0;
+
// Prevent sending error messages afterwards.
if (m_gdbAdapter)
disconnect(gdbProc(), 0, this, 0);
@@ -422,6 +479,7 @@ void GdbEngine::handleResponse(const QByteArray &buff)
}
if (pid)
notifyInferiorPid(pid);
+ handleThreadGroupCreated(result);
} else if (asyncClass == "thread-created") {
//"{id="1",group-id="28902"}"
QByteArray id = result.findChild("id").data();
@@ -430,6 +488,7 @@ void GdbEngine::handleResponse(const QByteArray &buff)
// Archer has "{id="28902"}"
QByteArray id = result.findChild("id").data();
showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
+ handleThreadGroupExited(result);
} else if (asyncClass == "thread-exited") {
//"{id="1",group-id="28902"}"
QByteArray id = result.findChild("id").data();
@@ -607,6 +666,34 @@ void GdbEngine::handleResponse(const QByteArray &buff)
// version and/or OS version used.
if (data.startsWith("warning:"))
showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
+
+ // Messages when the target exits (gdbserver)
+ if (data.trimmed() == "Remote connection closed"
+ || data.trimmed() == "Remote communication error. "
+ "Target disconnected.: No error."
+ || data.trimmed() == "Quit") {
+ notifyInferiorExited();
+ }
+
+ // From SuSE's gdb: >&"Missing separate debuginfo for ...\n"
+ // ">&"Try: zypper install -C \"debuginfo(build-id)=c084ee5876ed1ac12730181c9f07c3e027d8e943\"\n"
+ if (data.startsWith("Missing separate debuginfo for ")) {
+ m_lastMissingDebugInfo = QString::fromLocal8Bit(data.mid(32));
+ } else if (data.startsWith("Try: zypper")) {
+ QString cmd = QString::fromLocal8Bit(data.mid(4));
+
+ Task task(Task::Warning,
+ tr("Missing debug information for %1\nTry: %2")
+ .arg(m_lastMissingDebugInfo).arg(cmd),
+ Utils::FileName(), 0, Core::Id("Debuginfo"));
+
+ taskHub()->addTask(task);
+
+ DebugInfoTask dit;
+ dit.command = cmd;
+ m_debugInfoTaskHandler->addTask(task.taskId, dit);
+ }
+
break;
}
@@ -1025,10 +1112,6 @@ void GdbEngine::handleResultRecord(GdbResponse *response)
QTC_CHECK(state() == InferiorRunOk);
notifyInferiorSpontaneousStop();
notifyEngineIll();
- } else if (msg.startsWith("Remote connection closed")
- || msg.startsWith("Quit")) {
- // Can happen when the target exits (gdbserver)
- notifyInferiorExited();
} else {
// Windows: Some DLL or some function not found. Report
// the exception now in a box.
@@ -1171,7 +1254,6 @@ void GdbEngine::updateAll()
void GdbEngine::handleQuerySources(const GdbResponse &response)
{
m_sourcesListUpdating = false;
- m_sourcesListOutdated = false;
if (response.resultClass == GdbResultDone) {
QMap<QString, QString> oldShortToFull = m_shortToFullName;
m_shortToFullName.clear();
@@ -1180,16 +1262,17 @@ void GdbEngine::handleQuerySources(const GdbResponse &response)
// fullname="/data5/dev/ide/main/bin/dumper/dumper.cpp"},
GdbMi files = response.data.findChild("files");
foreach (const GdbMi &item, files.children()) {
- GdbMi fullName = item.findChild("fullname");
GdbMi fileName = item.findChild("file");
+ if (fileName.data().endsWith("<built-in>"))
+ continue;
+ GdbMi fullName = item.findChild("fullname");
QString file = QString::fromLocal8Bit(fileName.data());
+ QString full;
if (fullName.isValid()) {
- QString full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
- m_shortToFullName[file] = full;
+ full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
m_fullToShortName[full] = file;
- } else if (fileName.isValid()) {
- m_shortToFullName[file] = tr("<unknown>");
}
+ m_shortToFullName[file] = full;
}
if (m_shortToFullName != oldShortToFull)
sourceFilesHandler()->setSourceFiles(m_shortToFullName);
@@ -1722,6 +1805,11 @@ void GdbEngine::handleShowVersion(const GdbResponse &response)
postCommand("set target-async on", ConsoleCommand);
else
postCommand("set target-async off", ConsoleCommand);
+
+ if (startParameters().multiProcess)
+ postCommand("set detach-on-fork off", ConsoleCommand);
+
+ postCommand("set build-id-verbose 2", ConsoleCommand);
}
}
@@ -1846,6 +1934,43 @@ QString GdbEngine::cleanupFullName(const QString &fileName)
cleanFilePath.replace(0, startParameters().remoteMountPoint.length(),
startParameters().localMountDir);
}
+
+ if (!debuggerCore()->boolSetting(AutoEnrichParameters))
+ return cleanFilePath;
+
+ const QString sysroot = startParameters().sysroot;
+ if (QFileInfo(cleanFilePath).isReadable())
+ return cleanFilePath;
+ if (!sysroot.isEmpty() && fileName.startsWith(QLatin1Char('/'))) {
+ cleanFilePath = sysroot + fileName;
+ if (QFileInfo(cleanFilePath).isReadable())
+ return cleanFilePath;
+ }
+ if (m_baseNameToFullName.isEmpty()) {
+ QString debugSource = sysroot + QLatin1String("/usr/src/debug");
+ if (QFileInfo(debugSource).isDir()) {
+ QDirIterator it(debugSource, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ QString name = it.fileName();
+ if (!name.startsWith(QLatin1Char('.'))) {
+ QString path = it.filePath();
+ m_baseNameToFullName.insert(name, path);
+ }
+ }
+ }
+ }
+
+ cleanFilePath.clear();
+ const QString base = QFileInfo(fileName).fileName();
+
+ QMap<QString, QString>::const_iterator jt = m_baseNameToFullName.find(base);
+ while (jt != m_baseNameToFullName.end() && jt.key() == base) {
+ // FIXME: Use some heuristics to find the "best" match.
+ return jt.value();
+ //++jt;
+ }
+
return cleanFilePath;
}
@@ -1853,13 +1978,15 @@ void GdbEngine::shutdownInferior()
{
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
m_commandsToRunOnTemporaryBreak.clear();
- m_gdbAdapter->shutdownInferior();
-}
-
-void GdbEngine::defaultInferiorShutdown(const char *cmd)
-{
- QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
- postCommand(cmd, NeedsStop | LosesChild, CB(handleInferiorShutdown));
+ switch (startParameters().closeMode) {
+ case KillAtClose:
+ postCommand("kill", NeedsStop | LosesChild, CB(handleInferiorShutdown));
+ return;
+ case DetachAtClose:
+ postCommand("detach", NeedsStop | LosesChild, CB(handleInferiorShutdown));
+ return;
+ }
+ QTC_ASSERT(false, notifyInferiorShutdownFailed());
}
void GdbEngine::handleInferiorShutdown(const GdbResponse &response)
@@ -1946,6 +2073,18 @@ void GdbEngine::handleDetach(const GdbResponse &response)
notifyInferiorExited();
}
+void GdbEngine::handleThreadGroupCreated(const GdbMi &result)
+{
+ QByteArray id = result.findChild("id").data();
+ QByteArray pid = result.findChild("pid").data();
+ Q_UNUSED(pid);
+}
+
+void GdbEngine::handleThreadGroupExited(const GdbMi &result)
+{
+ QByteArray id = result.findChild("id").data();
+}
+
int GdbEngine::currentFrame() const
{
return stackHandler()->currentIndex();
@@ -3372,7 +3511,6 @@ void GdbEngine::examineModules()
void GdbEngine::invalidateSourcesList()
{
m_modulesListOutdated = true;
- m_sourcesListOutdated = true;
m_breakListOutdated = true;
}
@@ -5173,6 +5311,11 @@ void GdbEngine::scheduleTestResponse(int testCase, const QByteArray &response)
m_scheduledTestResponses[token] = response;
}
+void GdbEngine::requestDebugInformation(const DebugInfoTask &task)
+{
+ QProcess::startDetached(task.command);
+}
+
//
// Factory
//
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 08d4e0b17a..48551cd682 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -57,6 +57,8 @@ namespace Internal {
class AbstractGdbAdapter;
class AbstractGdbProcess;
+class DebugInfoTask;
+class DebugInfoTaskHandler;
class GdbResponse;
class GdbMi;
class GdbToolTipContext;
@@ -264,7 +266,6 @@ private: ////////// Gdb Process Management //////////
void handleNamespaceExtraction(const GdbResponse &response);
void handleAdapterStarted();
- void defaultInferiorShutdown(const char *cmd);
void loadInitScript();
void loadPythonDumpers();
void pythonDumpersFailed();
@@ -584,9 +585,9 @@ private: ////////// View & Data Stuff //////////
// awful hack to keep track of used files
QMap<QString, QString> m_shortToFullName;
QMap<QString, QString> m_fullToShortName;
+ QMultiMap<QString, QString> m_baseNameToFullName;
void invalidateSourcesList();
- bool m_sourcesListOutdated;
bool m_sourcesListUpdating;
bool m_breakListOutdated;
@@ -656,6 +657,9 @@ private: ////////// View & Data Stuff //////////
void handleDebuggingHelperVersionCheckClassic(const GdbResponse &response);
void handleDetach(const GdbResponse &response);
+ void handleThreadGroupCreated(const GdbMi &result);
+ void handleThreadGroupExited(const GdbMi &result);
+
Q_SLOT void createFullBacktrace();
void handleCreateFullBacktrace(const GdbResponse &response);
@@ -729,6 +733,7 @@ private: ////////// View & Data Stuff //////////
// HACK:
QByteArray m_currentThread;
QString m_lastWinException;
+ QString m_lastMissingDebugInfo;
BreakpointResponseId m_qFatalBreakpointResponseId;
bool m_actingOnExpectedStop;
@@ -736,6 +741,11 @@ private: ////////// View & Data Stuff //////////
QHash<int, QByteArray> m_scheduledTestResponses;
QSet<int> m_testCases;
+
+ // Debug information
+ friend class DebugInfoTaskHandler;
+ void requestDebugInformation(const DebugInfoTask &task);
+ DebugInfoTaskHandler *m_debugInfoTaskHandler;
};
} // namespace Internal
diff --git a/src/plugins/debugger/gdb/localplaingdbadapter.cpp b/src/plugins/debugger/gdb/localplaingdbadapter.cpp
index 0b0060ffa6..1e0ad9ff24 100644
--- a/src/plugins/debugger/gdb/localplaingdbadapter.cpp
+++ b/src/plugins/debugger/gdb/localplaingdbadapter.cpp
@@ -114,11 +114,6 @@ void LocalPlainGdbAdapter::runEngine()
AbstractPlainGdbAdapter::runEngine();
}
-void LocalPlainGdbAdapter::shutdownInferior()
-{
- m_engine->defaultInferiorShutdown("kill");
-}
-
void LocalPlainGdbAdapter::shutdownAdapter()
{
showMessage(_("PLAIN ADAPTER SHUTDOWN %1").arg(state()));
diff --git a/src/plugins/debugger/gdb/localplaingdbadapter.h b/src/plugins/debugger/gdb/localplaingdbadapter.h
index ced097faac..39181bac7e 100644
--- a/src/plugins/debugger/gdb/localplaingdbadapter.h
+++ b/src/plugins/debugger/gdb/localplaingdbadapter.h
@@ -58,7 +58,6 @@ private:
void setupInferior();
void runEngine();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
DumperHandling dumperHandling() const;
diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
index 9cd71bd3a9..174fbb9069 100644
--- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
+++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
@@ -233,9 +233,16 @@ void RemoteGdbServerAdapter::handleFileExecAndSymbols(const GdbResponse &respons
if (response.resultClass == GdbResultDone) {
callTargetRemote();
} else {
+ QByteArray reason = response.data.findChild("msg").data();
QString msg = tr("Reading debug information failed:\n");
- msg += QString::fromLocal8Bit(response.data.findChild("msg").data());
- m_engine->notifyInferiorSetupFailed(msg);
+ msg += QString::fromLocal8Bit(reason);
+ if (reason.endsWith("No such file or directory.")) {
+ showMessage(_("INFERIOR STARTUP: BINARY NOT FOUND"));
+ showMessage(msg, StatusBar);
+ callTargetRemote(); // Proceed nevertheless.
+ } else {
+ m_engine->notifyInferiorSetupFailed(msg);
+ }
}
}
@@ -247,14 +254,24 @@ void RemoteGdbServerAdapter::callTargetRemote()
// (1) connects to the gdb server
// (2) starts the remote application
// (3) stops the remote application (early, e.g. in the dynamic linker)
- QString channel = startParameters().remoteChannel;
- if (m_engine->m_isQnxGdb) {
- m_engine->postCommand("target qnx " + channel.toLatin1(),
- CB(handleTargetQnx));
- } else {
- m_engine->postCommand("target remote " + channel.toLatin1(),
- CB(handleTargetRemote));
+ QByteArray channel = startParameters().remoteChannel.toLatin1();
+
+ // Don't touch channels with explicitly set protocols.
+ if (!channel.startsWith("tcp:") && !channel.startsWith("udp:")
+ && !channel.startsWith("file:") && channel.contains(':'))
+ {
+ // "Fix" the IPv6 case with host names without '['...']'
+ if (!channel.startsWith('[') && channel.count(':') >= 2) {
+ channel.insert(0, '[');
+ channel.insert(channel.lastIndexOf(':'), ']');
+ }
+ channel = "tcp:" + channel;
}
+
+ if (m_engine->m_isQnxGdb)
+ m_engine->postCommand("target qnx " + channel, CB(handleTargetQnx));
+ else
+ m_engine->postCommand("target remote " + channel, CB(handleTargetRemote));
}
void RemoteGdbServerAdapter::handleTargetRemote(const GdbResponse &record)
@@ -355,14 +372,6 @@ void RemoteGdbServerAdapter::handleInterruptInferior(const GdbResponse &response
}
}
-void RemoteGdbServerAdapter::shutdownInferior()
-{
- if (m_engine->startParameters().startMode == AttachToRemoteServer)
- m_engine->defaultInferiorShutdown("detach");
- else
- m_engine->defaultInferiorShutdown("kill");
-}
-
void RemoteGdbServerAdapter::shutdownAdapter()
{
m_engine->notifyAdapterShutdownOk();
diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.h b/src/plugins/debugger/gdb/remotegdbserveradapter.h
index 9261797d6d..7a3f88d642 100644
--- a/src/plugins/debugger/gdb/remotegdbserveradapter.h
+++ b/src/plugins/debugger/gdb/remotegdbserveradapter.h
@@ -59,7 +59,6 @@ private:
void setupInferior();
void runEngine();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
diff --git a/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
index 5913ade032..a3a7263b14 100644
--- a/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
+++ b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
@@ -98,11 +98,6 @@ void RemotePlainGdbAdapter::handleApplicationOutput(const QByteArray &output)
showMessage(QString::fromUtf8(output), AppOutput);
}
-void RemotePlainGdbAdapter::shutdownInferior()
-{
- m_engine->defaultInferiorShutdown("kill");
-}
-
void RemotePlainGdbAdapter::shutdownAdapter()
{
m_engine->notifyAdapterShutdownOk();
diff --git a/src/plugins/debugger/gdb/remoteplaingdbadapter.h b/src/plugins/debugger/gdb/remoteplaingdbadapter.h
index b1e7644574..7c19e10df8 100644
--- a/src/plugins/debugger/gdb/remoteplaingdbadapter.h
+++ b/src/plugins/debugger/gdb/remoteplaingdbadapter.h
@@ -55,7 +55,6 @@ private:
void startAdapter();
void setupInferior();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
void handleRemoteSetupDone(int gdbServerPort, int qmlPort);
void handleRemoteSetupFailed(const QString &reason);
diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp
index 8acff6ede4..135630e875 100644
--- a/src/plugins/debugger/gdb/termgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/termgdbadapter.cpp
@@ -210,11 +210,6 @@ void TermGdbAdapter::stubExited()
m_engine->notifyEngineIll();
}
-void TermGdbAdapter::shutdownInferior()
-{
- m_engine->defaultInferiorShutdown("kill");
-}
-
void TermGdbAdapter::shutdownAdapter()
{
m_engine->notifyAdapterShutdownOk();
diff --git a/src/plugins/debugger/gdb/termgdbadapter.h b/src/plugins/debugger/gdb/termgdbadapter.h
index d9ecee078d..b56d969cd2 100644
--- a/src/plugins/debugger/gdb/termgdbadapter.h
+++ b/src/plugins/debugger/gdb/termgdbadapter.h
@@ -62,7 +62,6 @@ private:
void setupInferior();
void runEngine();
void interruptInferior();
- void shutdownInferior();
void shutdownAdapter();
AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
diff --git a/src/plugins/debugger/lldb/ipcenginehost.cpp b/src/plugins/debugger/lldb/ipcenginehost.cpp
index 1c6765f4fc..73a01839de 100644
--- a/src/plugins/debugger/lldb/ipcenginehost.cpp
+++ b/src/plugins/debugger/lldb/ipcenginehost.cpp
@@ -591,7 +591,7 @@ void IPCEngineHost::rpcCallback(quint64 f, QByteArray payload)
}
}
-void IPCEngineHost::m_stateChanged(const Debugger::DebuggerState &state)
+void IPCEngineHost::m_stateChanged(Debugger::DebuggerState state)
{
QByteArray p;
{
diff --git a/src/plugins/debugger/lldb/ipcenginehost.h b/src/plugins/debugger/lldb/ipcenginehost.h
index b75e1ceeaa..71756c02f2 100644
--- a/src/plugins/debugger/lldb/ipcenginehost.h
+++ b/src/plugins/debugger/lldb/ipcenginehost.h
@@ -124,7 +124,7 @@ protected:
public slots:
void rpcCallback(quint64 f, QByteArray payload = QByteArray());
private slots:
- void m_stateChanged(const Debugger::DebuggerState &state);
+ void m_stateChanged(Debugger::DebuggerState state);
void readyRead();
private:
IPCEngineGuest *m_localGuest;
diff --git a/src/plugins/debugger/moduleswindow.cpp b/src/plugins/debugger/moduleswindow.cpp
index 70948ed429..3c7daa1838 100644
--- a/src/plugins/debugger/moduleswindow.cpp
+++ b/src/plugins/debugger/moduleswindow.cpp
@@ -57,10 +57,9 @@
namespace Debugger {
namespace Internal {
-ModulesWindow::ModulesWindow(QWidget *parent)
- : BaseWindow(parent)
+ModulesTreeView::ModulesTreeView(QWidget *parent)
+ : BaseTreeView(parent)
{
- setWindowTitle(tr("Modules"));
setSortingEnabled(true);
setAlwaysAdjustColumnsAction(debuggerCore()->action(AlwaysAdjustModulesColumnWidths));
@@ -68,7 +67,7 @@ ModulesWindow::ModulesWindow(QWidget *parent)
SLOT(moduleActivated(QModelIndex)));
}
-void ModulesWindow::moduleActivated(const QModelIndex &index)
+void ModulesTreeView::moduleActivated(const QModelIndex &index)
{
DebuggerEngine *engine = debuggerCore()->currentEngine();
QTC_ASSERT(engine, return);
@@ -76,7 +75,7 @@ void ModulesWindow::moduleActivated(const QModelIndex &index)
engine->gotoLocation(index.sibling(index.row(), 1).data().toString());
}
-void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev)
+void ModulesTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QString name;
QString fileName;
@@ -174,5 +173,11 @@ void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
+ModulesWindow::ModulesWindow()
+ : BaseWindow(new ModulesTreeView)
+{
+ setWindowTitle(tr("Modules"));
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/moduleswindow.h b/src/plugins/debugger/moduleswindow.h
index 41a8d11ef8..e80ec0823c 100644
--- a/src/plugins/debugger/moduleswindow.h
+++ b/src/plugins/debugger/moduleswindow.h
@@ -38,12 +38,12 @@
namespace Debugger {
namespace Internal {
-class ModulesWindow : public BaseWindow
+class ModulesTreeView : public BaseTreeView
{
Q_OBJECT
public:
- explicit ModulesWindow(QWidget *parent = 0);
+ explicit ModulesTreeView(QWidget *parent = 0);
private slots:
void moduleActivated(const QModelIndex &index);
@@ -52,6 +52,12 @@ private:
void contextMenuEvent(QContextMenuEvent *ev);
};
+class ModulesWindow : public BaseWindow
+{
+public:
+ ModulesWindow();
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp
index 7ab699eb19..46d9cdcb9d 100644
--- a/src/plugins/debugger/qml/qmladapter.cpp
+++ b/src/plugins/debugger/qml/qmladapter.cpp
@@ -67,7 +67,7 @@ public:
QWeakPointer<DebuggerEngine> m_engine;
QmlDebuggerClient *m_qmlClient;
- QmlJsDebugClient::QDeclarativeEngineDebug *m_engineDebugClient;
+ QmlJsDebugClient::QmlEngineDebugClient *m_engineDebugClient;
QTimer m_connectionTimer;
QDeclarativeDebugConnection *m_conn;
QHash<QString, QmlDebuggerClient*> debugClients;
@@ -304,14 +304,24 @@ QHash<QString, Internal::QmlDebuggerClient*> QmlAdapter::debuggerClients()
return d->debugClients;
}
-QmlJsDebugClient::QDeclarativeEngineDebug *QmlAdapter::engineDebugClient() const
+QmlJsDebugClient::QmlEngineDebugClient *QmlAdapter::engineDebugClient() const
{
return d->m_engineDebugClient;
}
-void QmlAdapter::setEngineDebugClient(QmlJsDebugClient::QDeclarativeEngineDebug *client)
+void QmlAdapter::setEngineDebugClient(QmlJsDebugClient::QmlEngineDebugClient *client)
{
+ Internal::QmlEngine *engine =
+ qobject_cast<Internal::QmlEngine *>(d->m_engine.data());
+ if (engine && d->m_engineDebugClient)
+ disconnect(d->m_engineDebugClient, SIGNAL(result(quint32,QVariant,QByteArray)),
+ engine,
+ SLOT(expressionEvaluated(quint32,QVariant)));
d->m_engineDebugClient = client;
+ if (engine && d->m_engineDebugClient)
+ connect(d->m_engineDebugClient, SIGNAL(result(quint32,QVariant,QByteArray)),
+ engine,
+ SLOT(expressionEvaluated(quint32,QVariant)));
}
QmlJsDebugClient::QDebugMessageClient *QmlAdapter::messageClient() const
diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h
index 9abaf2d5aa..8851518836 100644
--- a/src/plugins/debugger/qml/qmladapter.h
+++ b/src/plugins/debugger/qml/qmladapter.h
@@ -40,7 +40,7 @@
#include <QAbstractSocket>
namespace QmlJsDebugClient {
-class QDeclarativeEngineDebug;
+class QmlEngineDebugClient;
class QDeclarativeDebugConnection;
class QDebugMessageClient;
}
@@ -76,8 +76,8 @@ public:
Internal::QmlDebuggerClient *activeDebuggerClient();
QHash<QString, Internal::QmlDebuggerClient*> debuggerClients();
- QmlJsDebugClient::QDeclarativeEngineDebug *engineDebugClient() const;
- void setEngineDebugClient(QmlJsDebugClient::QDeclarativeEngineDebug *client);
+ QmlJsDebugClient::QmlEngineDebugClient *engineDebugClient() const;
+ void setEngineDebugClient(QmlJsDebugClient::QmlEngineDebugClient *client);
QDebugMessageClient *messageClient() const;
diff --git a/src/plugins/debugger/qml/qmlcppengine.cpp b/src/plugins/debugger/qml/qmlcppengine.cpp
index 2ee94881b6..890789fe86 100644
--- a/src/plugins/debugger/qml/qmlcppengine.cpp
+++ b/src/plugins/debugger/qml/qmlcppengine.cpp
@@ -40,6 +40,9 @@
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
+#include <texteditor/itexteditor.h>
+#include <qmljseditor/qmljseditorconstants.h>
+#include <cppeditor/cppeditorconstants.h>
#include <QTimer>
#include <QMainWindow>
@@ -127,10 +130,20 @@ QmlCppEngine::~QmlCppEngine()
delete d;
}
+bool QmlCppEngine::canDisplayTooltip() const
+{
+ return d->m_cppEngine->canDisplayTooltip() || d->m_qmlEngine->canDisplayTooltip();
+}
+
bool QmlCppEngine::setToolTipExpression(const QPoint & mousePos,
TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
{
- return d->m_activeEngine->setToolTipExpression(mousePos, editor, ctx);
+ bool success = false;
+ if (editor->id() == CppEditor::Constants::CPPEDITOR_ID)
+ success = d->m_cppEngine->setToolTipExpression(mousePos, editor, ctx);
+ else if (editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID)
+ success = d->m_qmlEngine->setToolTipExpression(mousePos, editor, ctx);
+ return success;
}
void QmlCppEngine::updateWatchData(const WatchData &data,
@@ -216,9 +229,14 @@ bool QmlCppEngine::hasCapability(unsigned cap) const
// ### this could also be an OR of both engines' capabilities
bool hasCap = d->m_cppEngine->hasCapability(cap);
if (d->m_activeEngine != d->m_cppEngine) {
+ //Some capabilities cannot be handled by QML Engine
+ //Expand this list as and when required
if (cap == AddWatcherWhileRunningCapability)
hasCap = hasCap || d->m_qmlEngine->hasCapability(cap);
- if (cap == WatchWidgetsCapability)
+ if (cap == WatchWidgetsCapability ||
+ cap == DisassemblerCapability ||
+ cap == OperateByInstructionCapability ||
+ cap == ReverseSteppingCapability)
hasCap = hasCap && d->m_qmlEngine->hasCapability(cap);
}
return hasCap;
@@ -268,7 +286,7 @@ bool QmlCppEngine::acceptsBreakpoint(BreakpointModelId id) const
void QmlCppEngine::selectThread(int index)
{
- d->m_cppEngine->selectThread(index);
+ d->m_activeEngine->selectThread(index);
}
void QmlCppEngine::assignValueInDebugger(const WatchData *data,
diff --git a/src/plugins/debugger/qml/qmlcppengine.h b/src/plugins/debugger/qml/qmlcppengine.h
index efddd12f85..7d3483ed0a 100644
--- a/src/plugins/debugger/qml/qmlcppengine.h
+++ b/src/plugins/debugger/qml/qmlcppengine.h
@@ -50,6 +50,7 @@ public:
QString *errorMessage);
~QmlCppEngine();
+ bool canDisplayTooltip() const;
bool setToolTipExpression(const QPoint &mousePos,
TextEditor::ITextEditor * editor, const DebuggerToolTipContext &);
void updateWatchData(const WatchData &data,
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index abc050cf4c..f0e6ebc572 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -57,6 +57,7 @@
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/applicationlauncher.h>
#include <qmljsdebugclient/qdeclarativeoutputparser.h>
+#include <qmljsdebugclient/qmlenginedebugclient.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -119,13 +120,16 @@ private:
InteractiveInterpreter m_interpreter;
bool m_validContext;
QHash<QString,BreakpointModelId> pendingBreakpoints;
+ QList<quint32> queryIds;
bool m_retryOnConnectFail;
+ bool m_automaticConnect;
};
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
: m_adapter(q),
m_validContext(false),
- m_retryOnConnectFail(false)
+ m_retryOnConnectFail(false),
+ m_automaticConnect(false)
{}
class ASTWalker: public Visitor
@@ -367,6 +371,7 @@ QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
if (startParameters.useTerminal) {
d->m_noDebugOutputTimer.setInterval(0);
d->m_retryOnConnectFail = true;
+ d->m_automaticConnect = true;
}
}
@@ -397,6 +402,9 @@ void QmlEngine::setupInferior()
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
notifyInferiorSetupOk();
+
+ if (d->m_automaticConnect)
+ beginConnection();
}
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
@@ -421,12 +429,22 @@ void QmlEngine::tryToConnect(quint16 port)
{
showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
d->m_retryOnConnectFail = true;
- beginConnection(port);
+ if (state() == EngineRunRequested
+ && !d->m_automaticConnect)
+ beginConnection(port);
+ else
+ d->m_automaticConnect = true;
}
void QmlEngine::beginConnection(quint16 port)
{
d->m_noDebugOutputTimer.stop();
+
+ if (state() != EngineRunRequested && d->m_retryOnConnectFail)
+ return;
+
+ QTC_ASSERT(state() == EngineRunRequested, return)
+
if (port > 0) {
QTC_CHECK(startParameters().communicationChannel
== DebuggerStartParameters::CommunicationChannelTcpIp);
@@ -1038,23 +1056,15 @@ void QmlEngine::synchronizeWatchers()
}
}
-void QmlEngine::onDebugQueryStateChanged(
- QmlJsDebugClient::QDeclarativeDebugQuery::State state)
+void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result)
{
- QmlJsDebugClient::QDeclarativeDebugExpressionQuery *query =
- qobject_cast<QmlJsDebugClient::QDeclarativeDebugExpressionQuery *>(
- sender());
- if (query && state != QmlJsDebugClient::QDeclarativeDebugQuery::Error) {
+ if (d->queryIds.contains(queryId)) {
+ d->queryIds.removeOne(queryId);
QtMessageLogItem *item = constructLogItemTree(qtMessageLogHandler()->root(),
- query->result());
+ result);
if (item)
qtMessageLogHandler()->appendItem(item);
- } else
- qtMessageLogHandler()->
- appendItem(new QtMessageLogItem(qtMessageLogHandler()->root(),
- QtMessageLogHandler::ErrorType,
- _("Error evaluating expression.")));
- delete query;
+ }
}
bool QmlEngine::hasCapability(unsigned cap) const
@@ -1152,21 +1162,25 @@ bool QmlEngine::evaluateScriptExpression(const QString& expression)
//is sent to V8DebugService. In all other cases, the
//expression is evaluated by QDeclarativeEngine.
if (state() != InferiorStopOk) {
- QDeclarativeEngineDebug *engineDebug =
+ QmlEngineDebugClient *engineDebug =
d->m_adapter.engineDebugClient();
int id = d->m_adapter.currentSelectedDebugId();
if (engineDebug && id != -1) {
- QDeclarativeDebugExpressionQuery *query =
- engineDebug->queryExpressionResult(id, expression);
- connect(query,
- SIGNAL(stateChanged(
- QmlJsDebugClient::QDeclarativeDebugQuery
- ::State)),
- this,
- SLOT(onDebugQueryStateChanged(
- QmlJsDebugClient::QDeclarativeDebugQuery
- ::State)));
+ quint32 queryId =
+ engineDebug->queryExpressionResult(
+ id, expression);
+ if (queryId) {
+ d->queryIds << queryId;
+ } else {
+ didEvaluate = false;
+ qtMessageLogHandler()->
+ appendItem(
+ new QtMessageLogItem(
+ qtMessageLogHandler()->root(),
+ QtMessageLogHandler::ErrorType,
+ _("Error evaluating expression.")));
+ }
}
} else {
executeDebuggerCommand(expression);
diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h
index 3f285da4a3..5a4876b39c 100644
--- a/src/plugins/debugger/qml/qmlengine.h
+++ b/src/plugins/debugger/qml/qmlengine.h
@@ -34,7 +34,6 @@
#define DEBUGGER_QMLENGINE_H
#include "debuggerengine.h"
-#include <qmljsdebugclient/qdeclarativeenginedebug.h>
#include <qmljsdebugclient/qdebugmessageclient.h>
#include <utils/outputformat.h>
#include <qmljs/qmljsdocument.h>
@@ -58,7 +57,7 @@ namespace Internal {
class QtMessageLogItem;
class QmlEnginePrivate;
-class QmlEngine : public DebuggerEngine
+class DEBUGGER_EXPORT QmlEngine : public DebuggerEngine
{
Q_OBJECT
@@ -96,6 +95,7 @@ public:
public slots:
void disconnected();
void documentUpdated(QmlJS::Document::Ptr doc);
+ void expressionEvaluated(quint32 queryId, const QVariant &result);
private slots:
void errorMessageBoxFinished(int result);
@@ -171,8 +171,6 @@ private slots:
void appendMessage(const QString &msg, Utils::OutputFormat);
void synchronizeWatchers();
- void onDebugQueryStateChanged(
- QmlJsDebugClient::QDeclarativeDebugQuery::State state);
private:
void closeConnection();
diff --git a/src/plugins/debugger/qml/qmljsprivateapi.h b/src/plugins/debugger/qml/qmljsprivateapi.h
index a8654ba926..003938c548 100644
--- a/src/plugins/debugger/qml/qmljsprivateapi.h
+++ b/src/plugins/debugger/qml/qmljsprivateapi.h
@@ -32,8 +32,10 @@
#ifndef QMLJSPRIVATEAPI_H
#define QMLJSPRIVATEAPI_H
-#include <qmljsdebugclient/qdeclarativeenginedebug.h>
+#include <qmljsdebugclient/qmlenginedebugclient.h>
#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include <qmljsdebugclient/qdeclarativeengineclient.h>
+#include <qmljsdebugclient/qmldebuggerclient.h>
using namespace QmlJsDebugClient;
diff --git a/src/plugins/debugger/qtmessagelogeditor.cpp b/src/plugins/debugger/qtmessagelogeditor.cpp
index 62e3fdf821..cb198077d0 100644
--- a/src/plugins/debugger/qtmessagelogeditor.cpp
+++ b/src/plugins/debugger/qtmessagelogeditor.cpp
@@ -67,6 +67,7 @@ QtMessageLogEditor::QtMessageLogEditor(const QModelIndex &index,
format.setName(_("prompt"));
format.setHeight(9);
format.setWidth(9);
+ textCursor().insertText(_(" "));
textCursor().insertImage(format);
textCursor().insertText(_(" "));
m_startOfEditableArea = textCursor().position();
diff --git a/src/plugins/debugger/qtmessagelogitemdelegate.h b/src/plugins/debugger/qtmessagelogitemdelegate.h
index 7d35f50727..ffb76d85e2 100644
--- a/src/plugins/debugger/qtmessagelogitemdelegate.h
+++ b/src/plugins/debugger/qtmessagelogitemdelegate.h
@@ -180,7 +180,7 @@ private:
public:
static const int TASK_ICON_SIZE = 16;
- static const int ITEM_PADDING = 7;
+ static const int ITEM_PADDING = 8;
static const int ITEM_SPACING = 4;
};
diff --git a/src/plugins/debugger/qtmessagelogwindow.cpp b/src/plugins/debugger/qtmessagelogwindow.cpp
index 3be4b73af6..5a0a5d4e96 100644
--- a/src/plugins/debugger/qtmessagelogwindow.cpp
+++ b/src/plugins/debugger/qtmessagelogwindow.cpp
@@ -79,18 +79,17 @@ QtMessageLogWindow::QtMessageLogWindow(QWidget *parent)
vbox->setMargin(0);
vbox->setSpacing(0);
- QWidget *statusbarContainer = new QWidget();
+ QWidget *statusbarContainer = new Utils::StyledBar();
statusbarContainer->setFixedHeight(statusBarHeight);
QHBoxLayout *hbox = new QHBoxLayout(statusbarContainer);
hbox->setMargin(0);
+ hbox->setSpacing(5);
+ hbox->addSpacing(5);
- const int spacing = 7;
//Status Label
m_statusLabel = new Utils::StatusLabel;
- hbox->addSpacing(spacing);
hbox->addWidget(m_statusLabel);
hbox->addWidget(new Utils::StyledSeparator);
- hbox->addSpacing(spacing);
const int buttonWidth = 25;
//Filters
@@ -105,7 +104,6 @@ QtMessageLogWindow::QtMessageLogWindow(QWidget *parent)
m_showLogAction->setIcon(QIcon(_(":/debugger/images/log.png")));
button->setDefaultAction(m_showLogAction);
hbox->addWidget(button);
- hbox->addSpacing(spacing);
button = new QToolButton(this);
button->setAutoRaise(true);
@@ -118,7 +116,6 @@ QtMessageLogWindow::QtMessageLogWindow(QWidget *parent)
m_showWarningAction->setIcon(QIcon(_(":/debugger/images/warning.png")));
button->setDefaultAction(m_showWarningAction);
hbox->addWidget(button);
- hbox->addSpacing(spacing);
button = new QToolButton(this);
button->setAutoRaise(true);
@@ -131,7 +128,7 @@ QtMessageLogWindow::QtMessageLogWindow(QWidget *parent)
m_showErrorAction->setIcon(QIcon(_(":/debugger/images/error.png")));
button->setDefaultAction(m_showErrorAction);
hbox->addWidget(button);
- hbox->addSpacing(spacing);
+ hbox->addWidget(new Utils::StyledSeparator);
//Clear Button
button = new QToolButton;
@@ -141,7 +138,7 @@ QtMessageLogWindow::QtMessageLogWindow(QWidget *parent)
m_clearAction->setIcon(QIcon(_(Core::Constants::ICON_CLEAN_PANE)));
button->setDefaultAction(m_clearAction);
hbox->addWidget(button);
- hbox->addSpacing(spacing);
+ hbox->addWidget(new Utils::StyledSeparator);
m_treeView = new QtMessageLogView(this);
m_treeView->setSizePolicy(QSizePolicy::MinimumExpanding,
diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp
index 37608353bd..bf796bb43d 100644
--- a/src/plugins/debugger/registerwindow.cpp
+++ b/src/plugins/debugger/registerwindow.cpp
@@ -163,16 +163,13 @@ public:
//
///////////////////////////////////////////////////////////////////////
-RegisterWindow::RegisterWindow(QWidget *parent)
- : BaseWindow(parent)
+RegisterTreeView::RegisterTreeView(QWidget *parent)
+ : BaseTreeView(parent)
{
- setWindowTitle(tr("Registers"));
- setAlwaysAdjustColumnsAction(debuggerCore()->action(UseAlternatingRowColors));
setItemDelegate(new RegisterDelegate(this));
- setObjectName(QLatin1String("RegisterWindow"));
}
-void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev)
+void RegisterTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
@@ -271,11 +268,17 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
-void RegisterWindow::reloadRegisters()
+void RegisterTreeView::reloadRegisters()
{
// FIXME: Only trigger when becoming visible?
currentEngine()->reloadRegisters();
}
+RegisterWindow::RegisterWindow()
+ : BaseWindow(new RegisterTreeView)
+{
+ setWindowTitle(tr("Registers"));
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/registerwindow.h b/src/plugins/debugger/registerwindow.h
index 8a7253f62e..c6c9e587c4 100644
--- a/src/plugins/debugger/registerwindow.h
+++ b/src/plugins/debugger/registerwindow.h
@@ -38,12 +38,12 @@
namespace Debugger {
namespace Internal {
-class RegisterWindow : public BaseWindow
+class RegisterTreeView : public BaseTreeView
{
Q_OBJECT
public:
- explicit RegisterWindow(QWidget *parent = 0);
+ explicit RegisterTreeView(QWidget *parent = 0);
public slots:
void reloadRegisters();
@@ -52,6 +52,12 @@ private:
void contextMenuEvent(QContextMenuEvent *ev);
};
+class RegisterWindow : public BaseWindow
+{
+public:
+ RegisterWindow();
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/snapshotwindow.cpp b/src/plugins/debugger/snapshotwindow.cpp
index e29b7c6b2e..ebdc9ca0da 100644
--- a/src/plugins/debugger/snapshotwindow.cpp
+++ b/src/plugins/debugger/snapshotwindow.cpp
@@ -56,19 +56,19 @@ namespace Internal {
//
///////////////////////////////////////////////////////////////////////
-SnapshotWindow::SnapshotWindow(SnapshotHandler *handler)
+SnapshotTreeView::SnapshotTreeView(SnapshotHandler *handler)
{
m_snapshotHandler = handler;
setWindowTitle(tr("Snapshots"));
setAlwaysAdjustColumnsAction(debuggerCore()->action(AlwaysAdjustSnapshotsColumnWidths));
}
-void SnapshotWindow::rowActivated(const QModelIndex &index)
+void SnapshotTreeView::rowActivated(const QModelIndex &index)
{
m_snapshotHandler->activateSnapshot(index.row());
}
-void SnapshotWindow::keyPressEvent(QKeyEvent *ev)
+void SnapshotTreeView::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete) {
QItemSelectionModel *sm = selectionModel();
@@ -84,7 +84,7 @@ void SnapshotWindow::keyPressEvent(QKeyEvent *ev)
QTreeView::keyPressEvent(ev);
}
-void SnapshotWindow::contextMenuEvent(QContextMenuEvent *ev)
+void SnapshotTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QModelIndex idx = indexAt(ev->pos());
@@ -109,7 +109,7 @@ void SnapshotWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
-void SnapshotWindow::removeSnapshot(int i)
+void SnapshotTreeView::removeSnapshot(int i)
{
m_snapshotHandler->at(i)->quitDebugger();
}
diff --git a/src/plugins/debugger/snapshotwindow.h b/src/plugins/debugger/snapshotwindow.h
index 69c1c5df2b..302f4fd830 100644
--- a/src/plugins/debugger/snapshotwindow.h
+++ b/src/plugins/debugger/snapshotwindow.h
@@ -40,12 +40,12 @@ namespace Internal {
class SnapshotHandler;
-class SnapshotWindow : public BaseWindow
+class SnapshotTreeView : public BaseTreeView
{
Q_OBJECT
public:
- explicit SnapshotWindow(SnapshotHandler *handler);
+ explicit SnapshotTreeView(SnapshotHandler *handler);
private:
void rowActivated(const QModelIndex &index);
@@ -56,6 +56,16 @@ private:
SnapshotHandler *m_snapshotHandler;
};
+class SnapshotWindow : public BaseWindow
+{
+public:
+ explicit SnapshotWindow(SnapshotHandler *handler)
+ : BaseWindow(new SnapshotTreeView(handler))
+ {
+ setWindowTitle(tr("Snapshots"));
+ }
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/sourcefileswindow.cpp b/src/plugins/debugger/sourcefileswindow.cpp
index e950ef4665..78c623ee84 100644
--- a/src/plugins/debugger/sourcefileswindow.cpp
+++ b/src/plugins/debugger/sourcefileswindow.cpp
@@ -56,21 +56,20 @@
namespace Debugger {
namespace Internal {
-SourceFilesWindow::SourceFilesWindow(QWidget *parent)
- : BaseWindow(parent)
+SourceFilesTreeView::SourceFilesTreeView(QWidget *parent)
+ : BaseTreeView(parent)
{
- setWindowTitle(tr("Source Files"));
setSortingEnabled(true);
}
-void SourceFilesWindow::rowActivated(const QModelIndex &index)
+void SourceFilesTreeView::rowActivated(const QModelIndex &index)
{
DebuggerEngine *engine = debuggerCore()->currentEngine();
QTC_ASSERT(engine, return);
engine->gotoLocation(index.data().toString());
}
-void SourceFilesWindow::contextMenuEvent(QContextMenuEvent *ev)
+void SourceFilesTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
DebuggerEngine *engine = debuggerCore()->currentEngine();
QTC_ASSERT(engine, return);
@@ -107,6 +106,12 @@ void SourceFilesWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
+SourceFilesWindow::SourceFilesWindow()
+ : BaseWindow(new SourceFilesTreeView)
+{
+ setWindowTitle(tr("Source Files"));
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/sourcefileswindow.h b/src/plugins/debugger/sourcefileswindow.h
index 8f150febfe..23088ec6fc 100644
--- a/src/plugins/debugger/sourcefileswindow.h
+++ b/src/plugins/debugger/sourcefileswindow.h
@@ -38,18 +38,24 @@
namespace Debugger {
namespace Internal {
-class SourceFilesWindow : public BaseWindow
+class SourceFilesTreeView : public BaseTreeView
{
Q_OBJECT
public:
- SourceFilesWindow(QWidget *parent = 0);
+ SourceFilesTreeView(QWidget *parent = 0);
private:
void rowActivated(const QModelIndex &index);
void contextMenuEvent(QContextMenuEvent *ev);
};
+class SourceFilesWindow : public BaseWindow
+{
+public:
+ SourceFilesWindow();
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp
index 6c4fdbb62a..09841a7769 100644
--- a/src/plugins/debugger/stackframe.cpp
+++ b/src/plugins/debugger/stackframe.cpp
@@ -106,7 +106,26 @@ QString StackFrame::toToolTip() const
str << "<tr><td>" << tr("From:") << "</td><td>" << from << "</td></tr>";
if (!to.isEmpty())
str << "<tr><td>" << tr("To:") << "</td><td>" << to << "</td></tr>";
- str << "</table></body></html>";
+ str << "</table>";
+
+ str <<"<br> <br><i>" << tr("Note:") << " </i> ";
+ if (isUsable()) {
+ str << tr("Sources for this frame are available.<br>Double-click on "
+ "the file name to open an editor.");
+ } else if (line <= 0) {
+ str << tr("Binary debug information is not accessible for this "
+ "frame. This either means the core was not compiled "
+ "with debug information, or the debug information is not "
+ "accessible. Note that most distributions ship debug information "
+ "in separate packages.");
+ } else {
+ str << tr("Binary debug information is accessible for this "
+ "frame. However, matching sources have not been found. "
+ "Note that some distributions ship debug sources in "
+ "in separate packages.");
+ }
+
+ str << "</body></html>";
return res;
}
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
index 04efb34b3c..674aa8709c 100644
--- a/src/plugins/debugger/stackhandler.cpp
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -109,7 +109,7 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
case 2: // File name
return frame.file.isEmpty() ? frame.from : QFileInfo(frame.file).fileName();
case 3: // Line number
- return frame.line >= 0 ? QVariant(frame.line) : QVariant();
+ return frame.line > 0 ? QVariant(frame.line) : QVariant();
case 4: // Address
if (frame.address)
return QString::fromLatin1("0x%1").arg(frame.address, 0, 16);
diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp
index cd70765e58..cf8a59d6ca 100644
--- a/src/plugins/debugger/stackwindow.cpp
+++ b/src/plugins/debugger/stackwindow.cpp
@@ -59,8 +59,8 @@ static DebuggerEngine *currentEngine()
return debuggerCore()->currentEngine();
}
-StackWindow::StackWindow(QWidget *parent)
- : BaseWindow(parent)
+StackTreeView::StackTreeView(QWidget *parent)
+ : BaseTreeView(parent)
{
setWindowTitle(tr("Stack"));
setAlwaysAdjustColumnsAction(debuggerCore()->action(AlwaysAdjustStackColumnWidths));
@@ -74,19 +74,19 @@ StackWindow::StackWindow(QWidget *parent)
showAddressColumn(false);
}
-void StackWindow::showAddressColumn(bool on)
+void StackTreeView::showAddressColumn(bool on)
{
setColumnHidden(4, !on);
}
-void StackWindow::rowActivated(const QModelIndex &index)
+void StackTreeView::rowActivated(const QModelIndex &index)
{
currentEngine()->activateFrame(index.row());
}
-void StackWindow::setModel(QAbstractItemModel *model)
+void StackTreeView::setModel(QAbstractItemModel *model)
{
- BaseWindow::setModel(model);
+ BaseTreeView::setModel(model);
resizeColumnToContents(0);
resizeColumnToContents(3);
showAddressColumn(debuggerCore()->action(UseAddressInStackView)->isChecked());
@@ -100,8 +100,8 @@ static inline StackFrame inputFunctionForDisassembly()
StackFrame frame;
QInputDialog dialog;
dialog.setInputMode(QInputDialog::TextInput);
- dialog.setLabelText(StackWindow::tr("Function:"));
- dialog.setWindowTitle(StackWindow::tr("Disassemble Function"));
+ dialog.setLabelText(StackTreeView::tr("Function:"));
+ dialog.setWindowTitle(StackTreeView::tr("Disassemble Function"));
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
if (dialog.exec() != QDialog::Accepted)
return frame;
@@ -119,7 +119,7 @@ static inline StackFrame inputFunctionForDisassembly()
return frame;
}
-void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
+void StackTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
DebuggerEngine *engine = currentEngine();
StackHandler *handler = engine->stackHandler();
@@ -213,7 +213,7 @@ void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
-void StackWindow::copyContentsToClipboard()
+void StackTreeView::copyContentsToClipboard()
{
QString str;
int n = model()->rowCount();
@@ -233,7 +233,7 @@ void StackWindow::copyContentsToClipboard()
clipboard->setText(str, QClipboard::Clipboard);
}
-void StackWindow::reloadFullStack()
+void StackTreeView::reloadFullStack()
{
currentEngine()->reloadFullStack();
}
diff --git a/src/plugins/debugger/stackwindow.h b/src/plugins/debugger/stackwindow.h
index 1e7e73b942..239c043c0d 100644
--- a/src/plugins/debugger/stackwindow.h
+++ b/src/plugins/debugger/stackwindow.h
@@ -38,12 +38,12 @@
namespace Debugger {
namespace Internal {
-class StackWindow : public BaseWindow
+class StackTreeView : public BaseTreeView
{
Q_OBJECT
public:
- explicit StackWindow(QWidget *parent = 0);
+ explicit StackTreeView(QWidget *parent = 0);
private slots:
void showAddressColumn(bool on);
@@ -56,6 +56,15 @@ private:
void copyContentsToClipboard();
};
+class StackWindow : public BaseWindow
+{
+public:
+ StackWindow() : BaseWindow(new StackTreeView)
+ {
+ setWindowTitle(tr("Stack"));
+ }
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/threadswindow.cpp b/src/plugins/debugger/threadswindow.cpp
index d3065fa0ab..fb24b612ef 100644
--- a/src/plugins/debugger/threadswindow.cpp
+++ b/src/plugins/debugger/threadswindow.cpp
@@ -48,23 +48,20 @@
namespace Debugger {
namespace Internal {
-ThreadsWindow::ThreadsWindow(QWidget *parent)
- : BaseWindow(parent)
+ThreadsTreeView::ThreadsTreeView()
{
- setWindowTitle(tr("Thread"));
setSortingEnabled(true);
setAlwaysAdjustColumnsAction(debuggerCore()->action(AlwaysAdjustThreadsColumnWidths));
- setObjectName(QLatin1String("ThreadsWindow"));
}
-void ThreadsWindow::rowActivated(const QModelIndex &index)
+void ThreadsTreeView::rowActivated(const QModelIndex &index)
{
debuggerCore()->currentEngine()->selectThread(index.row());
}
-void ThreadsWindow::setModel(QAbstractItemModel *model)
+void ThreadsTreeView::setModel(QAbstractItemModel *model)
{
- BaseWindow::setModel(model);
+ BaseTreeView::setModel(model);
resizeColumnToContents(ThreadData::IdColumn);
resizeColumnToContents(ThreadData::LineColumn);
resizeColumnToContents(ThreadData::NameColumn);
@@ -72,7 +69,7 @@ void ThreadsWindow::setModel(QAbstractItemModel *model)
resizeColumnToContents(ThreadData::TargetIdColumn);
}
-void ThreadsWindow::contextMenuEvent(QContextMenuEvent *ev)
+void ThreadsTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
addBaseContextActions(&menu);
@@ -80,5 +77,12 @@ void ThreadsWindow::contextMenuEvent(QContextMenuEvent *ev)
handleBaseContextAction(act);
}
+ThreadsWindow::ThreadsWindow()
+ : BaseWindow(new ThreadsTreeView)
+{
+ setWindowTitle(tr("Threads"));
+ setObjectName(QLatin1String("ThreadsWindow"));
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/threadswindow.h b/src/plugins/debugger/threadswindow.h
index 9681c5ae04..15e1e152a6 100644
--- a/src/plugins/debugger/threadswindow.h
+++ b/src/plugins/debugger/threadswindow.h
@@ -38,12 +38,12 @@
namespace Debugger {
namespace Internal {
-class ThreadsWindow : public BaseWindow
+class ThreadsTreeView : public BaseTreeView
{
Q_OBJECT
public:
- ThreadsWindow(QWidget *parent = 0);
+ ThreadsTreeView();
private:
void rowActivated(const QModelIndex &index);
@@ -51,6 +51,12 @@ private:
void contextMenuEvent(QContextMenuEvent *ev);
};
+class ThreadsWindow : public BaseWindow
+{
+public:
+ ThreadsWindow();
+};
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index d95834e481..317a4887bc 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -47,7 +47,7 @@
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/abstracteditorsupport.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Overview.h>
#include <Symbols.h>
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
index 0350bb03e4..7379c02915 100644
--- a/src/plugins/debugger/watchwindow.cpp
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -83,7 +83,7 @@ static DebuggerEngine *currentEngine()
class WatchDelegate : public QItemDelegate
{
public:
- explicit WatchDelegate(WatchWindow *parent)
+ explicit WatchDelegate(WatchTreeView *parent)
: QItemDelegate(parent), m_watchWindow(parent)
{}
@@ -142,7 +142,7 @@ public:
}
private:
- WatchWindow *m_watchWindow;
+ WatchTreeView *m_watchWindow;
};
// Watch model query helpers.
@@ -199,10 +199,10 @@ static QString variableToolTip(const QString &name, const QString &type,
{
return offset ?
//: HTML tooltip of a variable in the memory editor
- WatchWindow::tr("<i>%1</i> %2 at #%3").
+ WatchTreeView::tr("<i>%1</i> %2 at #%3").
arg(type, name).arg(offset) :
//: HTML tooltip of a variable in the memory editor
- WatchWindow::tr("<i>%1</i> %2").arg(type, name);
+ WatchTreeView::tr("<i>%1</i> %2").arg(type, name);
}
static int memberVariableRecursion(const QAbstractItemModel *model,
@@ -318,7 +318,7 @@ static MemoryMarkupList
const quint64 offset = it.key() - address;
if (offset < size) {
ranges[offset] = ColorNumberToolTip(registerColorNumber,
- WatchWindow::tr("Register <i>%1</i>").arg(it.value()));
+ WatchTreeView::tr("Register <i>%1</i>").arg(it.value()));
} else {
break; // Sorted.
}
@@ -406,9 +406,9 @@ static void addVariableMemoryView(DebuggerEngine *engine, bool separateView,
const unsigned flags = separateView
? DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly : 0;
const QString title = deferencePointer
- ? WatchWindow::tr("Memory Referenced by Pointer \"%1\" (0x%2)")
+ ? WatchTreeView::tr("Memory Referenced by Pointer \"%1\" (0x%2)")
.arg(nameOf(m)).arg(address, 0, 16)
- : WatchWindow::tr("Memory at Variable \"%1\" (0x%2)")
+ : WatchTreeView::tr("Memory at Variable \"%1\" (0x%2)")
.arg(nameOf(m)).arg(address, 0, 16);
engine->openMemoryView(address, flags, markup, p, title, parent);
}
@@ -443,8 +443,8 @@ static void addStackLayoutMemoryView(DebuggerEngine *engine, bool separateView,
// Anything found and everything in a sensible range (static data in-between)?
if (end <= start || end - start > 100 * 1024) {
QMessageBox::information(parent,
- WatchWindow::tr("Cannot Display Stack Layout"),
- WatchWindow::tr("Could not determine a suitable address range."));
+ WatchTreeView::tr("Cannot Display Stack Layout"),
+ WatchTreeView::tr("Could not determine a suitable address range."));
return;
}
// Take a look at the register values. Extend the range a bit if suitable
@@ -468,7 +468,7 @@ static void addStackLayoutMemoryView(DebuggerEngine *engine, bool separateView,
const unsigned flags = separateView
? (DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly) : 0;
const QString title =
- WatchWindow::tr("Memory Layout of Local Variables at 0x%1").arg(start, 0, 16);
+ WatchTreeView::tr("Memory Layout of Local Variables at 0x%1").arg(start, 0, 16);
engine->openMemoryView(start, flags, markup, p, title, parent);
}
@@ -478,8 +478,8 @@ static void addStackLayoutMemoryView(DebuggerEngine *engine, bool separateView,
//
/////////////////////////////////////////////////////////////////////
-WatchWindow::WatchWindow(Type type, QWidget *parent)
- : BaseWindow(parent),
+WatchTreeView::WatchTreeView(Type type, QWidget *parent)
+ : BaseTreeView(parent),
m_type(type)
{
setObjectName(QLatin1String("WatchWindow"));
@@ -499,17 +499,17 @@ WatchWindow::WatchWindow(Type type, QWidget *parent)
SLOT(collapseNode(QModelIndex)));
}
-void WatchWindow::expandNode(const QModelIndex &idx)
+void WatchTreeView::expandNode(const QModelIndex &idx)
{
setModelData(LocalsExpandedRole, true, idx);
}
-void WatchWindow::collapseNode(const QModelIndex &idx)
+void WatchTreeView::collapseNode(const QModelIndex &idx)
{
setModelData(LocalsExpandedRole, false, idx);
}
-void WatchWindow::keyPressEvent(QKeyEvent *ev)
+void WatchTreeView::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete && m_type == WatchersType) {
QModelIndexList indices = selectionModel()->selectedRows();
@@ -533,7 +533,7 @@ void WatchWindow::keyPressEvent(QKeyEvent *ev)
QTreeView::keyPressEvent(ev);
}
-void WatchWindow::dragEnterEvent(QDragEnterEvent *ev)
+void WatchTreeView::dragEnterEvent(QDragEnterEvent *ev)
{
//QTreeView::dragEnterEvent(ev);
if (ev->mimeData()->hasText()) {
@@ -542,7 +542,7 @@ void WatchWindow::dragEnterEvent(QDragEnterEvent *ev)
}
}
-void WatchWindow::dragMoveEvent(QDragMoveEvent *ev)
+void WatchTreeView::dragMoveEvent(QDragMoveEvent *ev)
{
//QTreeView::dragMoveEvent(ev);
if (ev->mimeData()->hasText()) {
@@ -551,7 +551,7 @@ void WatchWindow::dragMoveEvent(QDragMoveEvent *ev)
}
}
-void WatchWindow::dropEvent(QDropEvent *ev)
+void WatchTreeView::dropEvent(QDropEvent *ev)
{
if (ev->mimeData()->hasText()) {
watchExpression(ev->mimeData()->text());
@@ -562,7 +562,7 @@ void WatchWindow::dropEvent(QDropEvent *ev)
//QTreeView::dropEvent(ev);
}
-void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
+void WatchTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
{
const QModelIndex idx = indexAt(ev->pos());
if (!idx.isValid()) {
@@ -577,24 +577,24 @@ void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
static QString addWatchActionText(QString exp)
{
if (exp.isEmpty())
- return WatchWindow::tr("Evaluate Expression");
+ return WatchTreeView::tr("Evaluate Expression");
if (exp.size() > 30) {
exp.truncate(30);
exp.append(QLatin1String("..."));
}
- return WatchWindow::tr("Evaluate Expression \"%1\"").arg(exp);
+ return WatchTreeView::tr("Evaluate Expression \"%1\"").arg(exp);
}
// Text for add watch action with truncated expression.
static QString removeWatchActionText(QString exp)
{
if (exp.isEmpty())
- return WatchWindow::tr("Remove Evaluated Expression");
+ return WatchTreeView::tr("Remove Evaluated Expression");
if (exp.size() > 30) {
exp.truncate(30);
exp.append(QLatin1String("..."));
}
- return WatchWindow::tr("Remove Evaluated Expression \"%1\"").arg(exp);
+ return WatchTreeView::tr("Remove Evaluated Expression \"%1\"").arg(exp);
}
static void copyToClipboard(const QString &clipboardText)
@@ -606,7 +606,7 @@ static void copyToClipboard(const QString &clipboardText)
clipboard->setText(clipboardText, QClipboard::Clipboard);
}
-void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
+void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
DebuggerEngine *engine = currentEngine();
WatchHandler *handler = engine->watchHandler();
@@ -970,7 +970,7 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
}
}
-bool WatchWindow::event(QEvent *ev)
+bool WatchTreeView::event(QEvent *ev)
{
if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
@@ -981,14 +981,14 @@ bool WatchWindow::event(QEvent *ev)
return QTreeView::event(ev);
}
-void WatchWindow::editItem(const QModelIndex &idx)
+void WatchTreeView::editItem(const QModelIndex &idx)
{
Q_UNUSED(idx) // FIXME
}
-void WatchWindow::setModel(QAbstractItemModel *model)
+void WatchTreeView::setModel(QAbstractItemModel *model)
{
- BaseWindow::setModel(model);
+ BaseTreeView::setModel(model);
setRootIsDecorated(true);
if (header()) {
header()->setDefaultAlignment(Qt::AlignLeft);
@@ -999,12 +999,12 @@ void WatchWindow::setModel(QAbstractItemModel *model)
connect(model, SIGNAL(layoutChanged()), SLOT(resetHelper()));
}
-void WatchWindow::resetHelper()
+void WatchTreeView::resetHelper()
{
resetHelper(model()->index(0, 0));
}
-void WatchWindow::resetHelper(const QModelIndex &idx)
+void WatchTreeView::resetHelper(const QModelIndex &idx)
{
if (idx.data(LocalsExpandedRole).toBool()) {
//qDebug() << "EXPANDING " << model()->data(idx, INameRole);
@@ -1022,24 +1022,24 @@ void WatchWindow::resetHelper(const QModelIndex &idx)
}
}
-void WatchWindow::watchExpression(const QString &exp)
+void WatchTreeView::watchExpression(const QString &exp)
{
currentEngine()->watchHandler()->watchExpression(exp);
}
-void WatchWindow::removeWatchExpression(const QString &exp)
+void WatchTreeView::removeWatchExpression(const QString &exp)
{
currentEngine()->watchHandler()->removeWatchExpression(exp);
}
-void WatchWindow::setModelData
+void WatchTreeView::setModelData
(int role, const QVariant &value, const QModelIndex &index)
{
QTC_ASSERT(model(), return);
model()->setData(index, value, role);
}
-void WatchWindow::setWatchpointAtAddress(quint64 address, unsigned size)
+void WatchTreeView::setWatchpointAtAddress(quint64 address, unsigned size)
{
BreakpointParameters data(WatchpointAtAddress);
data.address = address;
@@ -1053,7 +1053,7 @@ void WatchWindow::setWatchpointAtAddress(quint64 address, unsigned size)
breakHandler()->appendBreakpoint(data);
}
-void WatchWindow::setWatchpointAtExpression(const QString &exp)
+void WatchTreeView::setWatchpointAtExpression(const QString &exp)
{
BreakpointParameters data(WatchpointAtExpression);
data.expression = exp;
@@ -1066,6 +1066,7 @@ void WatchWindow::setWatchpointAtExpression(const QString &exp)
breakHandler()->appendBreakpoint(data);
}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h
index bf6658f389..b04f85c3f5 100644
--- a/src/plugins/debugger/watchwindow.h
+++ b/src/plugins/debugger/watchwindow.h
@@ -44,15 +44,16 @@ namespace Internal {
//
/////////////////////////////////////////////////////////////////////
-class WatchWindow : public BaseWindow
+class WatchTreeView : public BaseTreeView
{
Q_OBJECT
public:
enum Type { ReturnType, LocalsType, TooltipType, WatchersType };
- explicit WatchWindow(Type type, QWidget *parent = 0);
+ explicit WatchTreeView(Type type, QWidget *parent = 0);
Type type() const { return m_type; }
+ void setModel(QAbstractItemModel *model);
public slots:
void watchExpression(const QString &exp);
@@ -63,7 +64,6 @@ private:
Q_SLOT void expandNode(const QModelIndex &idx);
Q_SLOT void collapseNode(const QModelIndex &idx);
- void setModel(QAbstractItemModel *model);
void keyPressEvent(QKeyEvent *ev);
void contextMenuEvent(QContextMenuEvent *ev);
void dragEnterEvent(QDragEnterEvent *ev);
@@ -84,6 +84,15 @@ private:
bool m_grabbing;
};
+class WatchWindow : public BaseWindow
+{
+public:
+ explicit WatchWindow(WatchTreeView::Type type)
+ : BaseWindow(new WatchTreeView(type))
+ {
+ setWindowTitle(tr("Locals and Expressions"));
+ }
+};
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp
index 0cf6eb48df..d6f150d550 100644
--- a/src/plugins/designer/codemodelhelpers.cpp
+++ b/src/plugins/designer/codemodelhelpers.cpp
@@ -32,7 +32,7 @@
#include "codemodelhelpers.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/CoreTypes.h>
#include <cplusplus/Name.h>
diff --git a/src/plugins/designer/formeditorplugin.h b/src/plugins/designer/formeditorplugin.h
index a6a9bab959..6d11abeb03 100644
--- a/src/plugins/designer/formeditorplugin.h
+++ b/src/plugins/designer/formeditorplugin.h
@@ -41,6 +41,7 @@ namespace Internal {
class FormEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Designer.json")
public:
FormEditorPlugin();
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index e1ebe0cabd..e0b5724823 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -43,7 +43,7 @@
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/insertionpointlocator.h>
#include <cpptools/symbolfinder.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/Overview.h>
#include <cplusplus/CoreTypes.h>
diff --git a/src/plugins/designer/qtcreatorintegration.h b/src/plugins/designer/qtcreatorintegration.h
index 87b52e5ca1..50e97bb5d9 100644
--- a/src/plugins/designer/qtcreatorintegration.h
+++ b/src/plugins/designer/qtcreatorintegration.h
@@ -33,7 +33,7 @@
#ifndef QTCREATORINTEGRATION_H
#define QTCREATORINTEGRATION_H
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#if QT_VERSION >= 0x050000
# include <QDesignerIntegration>
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 350d9b58a5..3253d9685b 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -78,8 +78,6 @@
#include <utils/treewidgetcolumnstretcher.h>
#include <utils/stylehelper.h>
-#include <cppeditor/cppeditorconstants.h>
-
#include <cpptools/cpptoolsconstants.h>
#include <QAbstractTableModel>
@@ -895,7 +893,7 @@ FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
QRegExp("^cn(ext)?!?( (.*))?$");
- defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
+ defaultExCommandMap()[TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR] =
QRegExp("^tag?$");
defaultExCommandMap()[Core::Constants::GO_BACK] =
QRegExp("^pop?$");
diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h
index 88f60f619c..c7dba08b42 100644
--- a/src/plugins/fakevim/fakevimplugin.h
+++ b/src/plugins/fakevim/fakevimplugin.h
@@ -44,6 +44,7 @@ class FakeVimPluginPrivate;
class FakeVimPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "FakeVim.json")
public:
FakeVimPlugin();
diff --git a/src/plugins/find/findplugin.h b/src/plugins/find/findplugin.h
index f5862087db..9a34b9a3c9 100644
--- a/src/plugins/find/findplugin.h
+++ b/src/plugins/find/findplugin.h
@@ -54,6 +54,7 @@ class CurrentDocumentFind;
class FIND_EXPORT FindPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Find.json")
public:
FindPlugin();
diff --git a/src/plugins/find/treeviewfind.cpp b/src/plugins/find/treeviewfind.cpp
index 134f0d6c3e..b74c97564a 100644
--- a/src/plugins/find/treeviewfind.cpp
+++ b/src/plugins/find/treeviewfind.cpp
@@ -39,11 +39,10 @@ namespace Find {
struct ItemModelFindPrivate
{
- explicit ItemModelFindPrivate(QTreeView *view, int role, int column)
+ explicit ItemModelFindPrivate(QTreeView *view, int role)
: m_view(view)
, m_incrementalWrappedState(false),
- m_role(role),
- m_column(column)
+ m_role(role)
{
}
@@ -51,11 +50,10 @@ struct ItemModelFindPrivate
QModelIndex m_incrementalFindStart;
bool m_incrementalWrappedState;
int m_role;
- int m_column;
};
-TreeViewFind::TreeViewFind(QTreeView *view, int role, int column)
- : d(new ItemModelFindPrivate(view, role, column))
+TreeViewFind::TreeViewFind(QTreeView *view, int role)
+ : d(new ItemModelFindPrivate(view, role))
{
}
@@ -166,14 +164,17 @@ IFindSupport::Result TreeViewFind::find(const QString &searchTxt,
QModelIndex resultIndex;
QModelIndex currentIndex = d->m_view->currentIndex();
QModelIndex index = currentIndex;
+ int currentRow = currentIndex.row();
+
bool backward = (flags & QTextDocument::FindBackward);
if (wrapped)
*wrapped = false;
bool anyWrapped = false;
bool stepWrapped = false;
-
if (!startFromCurrentIndex)
index = followingIndex(index, backward, &stepWrapped);
+ else
+ currentRow = -1;
do {
anyWrapped |= stepWrapped; // update wrapped state if we actually stepped to next/prev item
if (index.isValid()) {
@@ -184,11 +185,17 @@ IFindSupport::Result TreeViewFind::find(const QString &searchTxt,
QRegExp searchExpr = QRegExp(searchTxt,
(sensitive ? Qt::CaseSensitive :
Qt::CaseInsensitive));
- if (searchExpr.indexIn(text) != -1)
+ if (searchExpr.indexIn(text) != -1
+ && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
+ && currentRow != index.row())
resultIndex = index;
} else {
QTextDocument doc(text);
- if (!doc.find(searchTxt, 0, flags).isNull())
+ if (!doc.find(searchTxt, 0,
+ flags & (Find::FindCaseSensitively |
+ Find::FindWholeWords)).isNull()
+ && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
+ && currentRow != index.row())
resultIndex = index;
}
}
@@ -226,17 +233,23 @@ QModelIndex TreeViewFind::nextIndex(const QModelIndex &idx, bool *wrapped) const
QModelIndex current = idx;
while (!nextIndex.isValid()) {
int row = current.row();
+ int column = current.column();
current = current.parent();
- if (row + 1 < model->rowCount(current)) {
- // Same parent has another child
- nextIndex = model->index(row + 1, 0, current);
+
+ if (column + 1 < model->columnCount(current)) {
+ nextIndex = model->index(row, column + 1, current);
} else {
- // go up one parent
- if (!current.isValid()) {
- // we start from the beginning
- if (wrapped)
- *wrapped = true;
- nextIndex = model->index(0, 0);
+ if (row + 1 < model->rowCount(current)) {
+ // Same parent has another child
+ nextIndex = model->index(row + 1, 0, current);
+ } else {
+ // go up one parent
+ if (!current.isValid()) {
+ // we start from the beginning
+ if (wrapped)
+ *wrapped = true;
+ nextIndex = model->index(0, 0);
+ }
}
}
}
@@ -252,21 +265,27 @@ QModelIndex TreeViewFind::prevIndex(const QModelIndex &idx, bool *wrapped) const
QAbstractItemModel *model = d->m_view->model();
if (current.isValid()) {
int row = current.row();
- if (row > 0) {
- current = model->index(row - 1, 0, current.parent());
+ int column = current.column();
+ if (column > 0) {
+ current = model->index(row, column - 1, current.parent());
} else {
- current = current.parent();
- checkForChildren = !current.isValid();
- if (checkForChildren && wrapped) {
- // we start from the end
- *wrapped = true;
+ if (row > 0) {
+ current = model->index(row - 1, model->columnCount(current.parent()) - 1,
+ current.parent());
+ } else {
+ current = current.parent();
+ checkForChildren = !current.isValid();
+ if (checkForChildren && wrapped) {
+ // we start from the end
+ *wrapped = true;
+ }
}
}
}
if (checkForChildren) {
// traverse down the hierarchy
while (int rc = model->rowCount(current)) {
- current = model->index(rc - 1, d->m_column, current);
+ current = model->index(rc - 1, model->columnCount(current) - 1, current);
}
}
return current;
diff --git a/src/plugins/find/treeviewfind.h b/src/plugins/find/treeviewfind.h
index 15fbbb361d..5c218512ba 100644
--- a/src/plugins/find/treeviewfind.h
+++ b/src/plugins/find/treeviewfind.h
@@ -48,8 +48,7 @@ class FIND_EXPORT TreeViewFind : public Find::IFindSupport
{
Q_OBJECT
public:
- explicit TreeViewFind(QTreeView *view, int role = Qt::DisplayRole,
- int column = 0);
+ explicit TreeViewFind(QTreeView *view, int role = Qt::DisplayRole);
virtual ~TreeViewFind();
bool supportsReplace() const;
diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp
index db917e4249..a7ea37a461 100644
--- a/src/plugins/genericprojectmanager/genericproject.cpp
+++ b/src/plugins/genericprojectmanager/genericproject.cpp
@@ -42,7 +42,7 @@
#include <projectexplorer/customexecutablerunconfiguration.h>
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
@@ -430,11 +430,6 @@ IProjectManager *GenericProject::projectManager() const
return m_manager;
}
-QList<Project *> GenericProject::dependsOn()
-{
- return QList<Project *>();
-}
-
QList<BuildConfigWidget*> GenericProject::subConfigWidgets()
{
QList<BuildConfigWidget*> list;
diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h
index 80517fc151..f35d7eb611 100644
--- a/src/plugins/genericprojectmanager/genericproject.h
+++ b/src/plugins/genericprojectmanager/genericproject.h
@@ -86,8 +86,6 @@ public:
ProjectExplorer::IProjectManager *projectManager() const;
GenericTarget *activeTarget() const;
- QList<ProjectExplorer::Project *> dependsOn();
-
QList<ProjectExplorer::BuildConfigWidget*> subConfigWidgets();
GenericProjectNode *rootProjectNode() const;
diff --git a/src/plugins/genericprojectmanager/genericprojectplugin.h b/src/plugins/genericprojectmanager/genericprojectplugin.h
index 277d219d55..29b1477fa9 100644
--- a/src/plugins/genericprojectmanager/genericprojectplugin.h
+++ b/src/plugins/genericprojectmanager/genericprojectplugin.h
@@ -51,6 +51,7 @@ class ProjectFilesFactory;
class GenericProjectPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "GenericProjectManager.json")
public:
GenericProjectPlugin();
diff --git a/src/plugins/git/commitdata.cpp b/src/plugins/git/commitdata.cpp
index dc485e1410..b18e9c6e53 100644
--- a/src/plugins/git/commitdata.cpp
+++ b/src/plugins/git/commitdata.cpp
@@ -58,6 +58,7 @@ void GitSubmitEditorPanelData::clear()
{
author.clear();
email.clear();
+ bypassHooks = false;
}
QString GitSubmitEditorPanelData::authorString() const
@@ -76,7 +77,8 @@ QString GitSubmitEditorPanelData::authorString() const
QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &data)
{
- d.nospace() << " author:" << data.author << " email: " << data.email;
+ d.nospace() << " author:" << data.author << " email: " << data.email
+ << " bypass hooks: " << data.bypassHooks;
return d;
}
diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h
index 1d469ff7cc..7795dd720f 100644
--- a/src/plugins/git/commitdata.h
+++ b/src/plugins/git/commitdata.h
@@ -62,6 +62,7 @@ struct GitSubmitEditorPanelData
QString author;
QString email;
+ bool bypassHooks;
};
QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &);
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 696560a783..dab54a13e6 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -1519,7 +1519,7 @@ static inline QString trimFileSpecification(QString fileSpec)
}
GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory, bool untracked,
- QString *output, QString *errorMessage, bool *onBranch)
+ QString *output, QString *errorMessage)
{
// Run 'status'. Note that git returns exitcode 1 if there are no added files.
QByteArray outputText;
@@ -1537,8 +1537,6 @@ GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory, bo
static const char * NO_BRANCH = "## HEAD (no branch)\n";
const bool branchKnown = !outputText.startsWith(NO_BRANCH);
- if (onBranch)
- *onBranch = branchKnown;
// Is it something really fatal?
if (!statusRc && !branchKnown) {
if (errorMessage) {
@@ -1605,6 +1603,13 @@ void GitClient::launchGitK(const QString &workingDirectory)
tryLauchingGitK(env, workingDirectory, foundBinDir.path() + QLatin1String("/bin"), false);
}
+void GitClient::launchRepositoryBrowser(const QString &workingDirectory)
+{
+ const QString repBrowserBinary = settings()->stringValue(GitSettings::repositoryBrowserCmd);
+ if (!repBrowserBinary.isEmpty())
+ QProcess::startDetached(repBrowserBinary, QStringList(workingDirectory), workingDirectory);
+}
+
bool GitClient::tryLauchingGitK(const QProcessEnvironment &env,
const QString &workingDirectory,
const QString &gitBinDirectory,
@@ -1688,15 +1693,10 @@ bool GitClient::getCommitData(const QString &workingDirectory,
}
// Run status. Note that it has exitcode 1 if there are no added files.
- bool onBranch;
QString output;
- const StatusResult status = gitStatus(repoDirectory, true, &output, errorMessage, &onBranch);
+ const StatusResult status = gitStatus(repoDirectory, true, &output, errorMessage);
switch (status) {
case StatusChanged:
- if (!onBranch) {
- *errorMessage = tr("You did not checkout a branch.");
- return false;
- }
break;
case StatusUnchanged:
if (amend)
@@ -1867,6 +1867,8 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory,
const QString &authorString = data.authorString();
if (!authorString.isEmpty())
args << QLatin1String("--author") << authorString;
+ if (data.bypassHooks)
+ args << QLatin1String("--no-verify");
QByteArray outputText;
QByteArray errorText;
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 00bad95e53..0110852238 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -212,9 +212,11 @@ public:
StatusResult gitStatus(const QString &workingDirectory,
bool untracked = false,
QString *output = 0,
- QString *errorMessage = 0, bool *onBranch = 0);
+ QString *errorMessage = 0);
void launchGitK(const QString &workingDirectory);
+ void launchRepositoryBrowser(const QString &workingDirectory);
+
QStringList synchronousRepositoryBranches(const QString &repositoryURL);
GitSettings *settings() const;
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index b845f4db34..3eec1999d8 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -430,6 +430,11 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
tr("Launch gitk"), Core::Id("Git.LaunchGitK"),
globalcontext, true, &GitClient::launchGitK);
+ m_repositoryBrowserAction
+ = createRepositoryAction(actionManager, gitContainer,
+ tr("Launch repository browser"), Core::Id("Git.LaunchRepositoryBrowser"),
+ globalcontext, true, &GitClient::launchRepositoryBrowser).first;
+
createRepositoryAction(actionManager, gitContainer,
tr("Branches..."), Core::Id("Git.BranchList"),
globalcontext, true, SLOT(branchList()));
@@ -1031,11 +1036,19 @@ void GitPlugin::updateActions(VcsBase::VcsBasePlugin::ActionState as)
foreach (QAction *repositoryAction, m_repositoryActions)
repositoryAction->setEnabled(repositoryEnabled);
+ updateRepositoryBrowserAction();
// Prompts for repo.
m_showAction->setEnabled(true);
}
+void GitPlugin::updateRepositoryBrowserAction()
+{
+ const bool repositoryEnabled = currentState().hasTopLevel();
+ const bool hasRepositoryBrowserCmd = !settings().stringValue(GitSettings::repositoryBrowserCmd).isEmpty();
+ m_repositoryBrowserAction->setEnabled(repositoryEnabled && hasRepositoryBrowserCmd);
+}
+
void GitPlugin::showCommit()
{
const VcsBase::VcsBasePluginState state = currentState();
@@ -1070,6 +1083,7 @@ void GitPlugin::setSettings(const GitSettings &s)
m_settings = s;
m_gitClient->saveSettings();
static_cast<GitVersionControl *>(versionControl())->emitConfigurationChanged();
+ updateRepositoryBrowserAction();
}
GitClient *GitPlugin::gitClient() const
diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h
index e8c3d15f0c..9ad5f2c344 100644
--- a/src/plugins/git/gitplugin.h
+++ b/src/plugins/git/gitplugin.h
@@ -86,6 +86,7 @@ typedef QPair<Utils::ParameterAction *, Core::Command* > ParameterActionCommandP
class GitPlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Git.json")
public:
GitPlugin();
@@ -174,6 +175,7 @@ private:
const Core::Context &context,
bool addToLocator, GitClientMemberFunc);
+ void updateRepositoryBrowserAction();
bool isCommitEditorOpen() const;
Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend);
void cleanCommitMessageFile();
@@ -192,6 +194,7 @@ private:
QAction *m_undoAction;
QAction *m_redoAction;
QAction *m_menuAction;
+ QAction *m_repositoryBrowserAction;
QVector<Utils::ParameterAction *> m_fileActions;
QVector<Utils::ParameterAction *> m_projectActions;
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 177d79a270..095922306c 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -49,6 +49,7 @@ const QLatin1String GitSettings::winSetHomeEnvironmentKey("WinSetHomeEnvironment
const QLatin1String GitSettings::showPrettyFormatKey("DiffPrettyFormat");
const QLatin1String GitSettings::gitkOptionsKey("GitKOptions");
const QLatin1String GitSettings::logDiffKey("LogDiff");
+const QLatin1String GitSettings::repositoryBrowserCmd("RepositoryBrowserCmd");
GitSettings::GitSettings()
{
@@ -70,6 +71,7 @@ GitSettings::GitSettings()
declareKey(gitkOptionsKey, QString());
declareKey(showPrettyFormatKey, 2);
declareKey(logDiffKey, false);
+ declareKey(repositoryBrowserCmd, QString());
}
QString GitSettings::gitBinaryPath(bool *ok, QString *errorMessage) const
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index e381dd014a..a6929bd5fd 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -56,6 +56,7 @@ public:
static const QLatin1String showPrettyFormatKey;
static const QLatin1String gitkOptionsKey;
static const QLatin1String logDiffKey;
+ static const QLatin1String repositoryBrowserCmd;
QString gitBinaryPath(bool *ok = 0, QString *errorMessage = 0) const;
diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp
index b3e782698e..36a1087fec 100644
--- a/src/plugins/git/gitsubmiteditorwidget.cpp
+++ b/src/plugins/git/gitsubmiteditorwidget.cpp
@@ -143,6 +143,7 @@ GitSubmitEditorPanelData GitSubmitEditorWidget::panelData() const
GitSubmitEditorPanelData rc;
rc.author = m_gitSubmitPanelUi.authorLineEdit->text();
rc.email = m_gitSubmitPanelUi.emailLineEdit->text();
+ rc.bypassHooks = m_gitSubmitPanelUi.bypassHooksCheckBox->isChecked();
return rc;
}
@@ -150,6 +151,7 @@ void GitSubmitEditorWidget::setPanelData(const GitSubmitEditorPanelData &data)
{
m_gitSubmitPanelUi.authorLineEdit->setText(data.author);
m_gitSubmitPanelUi.emailLineEdit->setText(data.email);
+ m_gitSubmitPanelUi.bypassHooksCheckBox->setChecked(data.bypassHooks);
authorInformationChanged();
}
diff --git a/src/plugins/git/gitsubmitpanel.ui b/src/plugins/git/gitsubmitpanel.ui
index c790299aa1..7830c490d8 100644
--- a/src/plugins/git/gitsubmitpanel.ui
+++ b/src/plugins/git/gitsubmitpanel.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>364</width>
- <height>172</height>
+ <height>177</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -60,23 +60,6 @@
<string>Commit Information</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="authorLabel">
- <property name="text">
- <string>Author:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="authorLineEdit">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
<item row="0" column="2">
<widget class="QLabel" name="invalidAuthorLabel">
<property name="minimumSize">
@@ -93,22 +76,6 @@
</property>
</widget>
</item>
- <item row="0" column="3">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>5</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
<item row="1" column="0">
<widget class="QLabel" name="emailLabel">
<property name="text">
@@ -142,6 +109,46 @@
</property>
</widget>
</item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="authorLabel">
+ <property name="text">
+ <string>Author:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="authorLineEdit">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>5</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="bypassHooksCheckBox">
+ <property name="text">
+ <string>By&amp;pass hooks</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp
index bcb3ecedd3..5c023a3358 100644
--- a/src/plugins/git/settingspage.cpp
+++ b/src/plugins/git/settingspage.cpp
@@ -36,6 +36,7 @@
#include "gitclient.h"
#include <vcsbase/vcsbaseconstants.h>
+#include <utils/pathchooser.h>
#include <QCoreApplication>
#include <QDir>
@@ -65,6 +66,8 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) :
#else
m_ui.winHomeCheckBox->setVisible(false);
#endif
+ m_ui.repBrowserCommandPathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ m_ui.repBrowserCommandPathChooser->setPromptDialogTitle(tr("Git Repository Browser Command"));
}
GitSettings SettingsPageWidget::settings() const
@@ -77,6 +80,7 @@ GitSettings SettingsPageWidget::settings() const
rc.setValue(GitSettings::promptOnSubmitKey, m_ui.promptToSubmitCheckBox->isChecked());
rc.setValue(GitSettings::winSetHomeEnvironmentKey, m_ui.winHomeCheckBox->isChecked());
rc.setValue(GitSettings::gitkOptionsKey, m_ui.gitkOptionsLineEdit->text().trimmed());
+ rc.setValue(GitSettings::repositoryBrowserCmd, m_ui.repBrowserCommandPathChooser->path().trimmed());
return rc;
}
@@ -89,6 +93,7 @@ void SettingsPageWidget::setSettings(const GitSettings &s)
m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(GitSettings::promptOnSubmitKey));
m_ui.winHomeCheckBox->setChecked(s.boolValue(GitSettings::winSetHomeEnvironmentKey));
m_ui.gitkOptionsLineEdit->setText(s.stringValue(GitSettings::gitkOptionsKey));
+ m_ui.repBrowserCommandPathChooser->setPath(s.stringValue(GitSettings::repositoryBrowserCmd));
}
QString SettingsPageWidget::searchKeywords() const
@@ -105,6 +110,8 @@ QString SettingsPageWidget::searchKeywords() const
<< sep << m_ui.promptToSubmitCheckBox->text()
<< sep << m_ui.gitkGroupBox->title()
<< sep << m_ui.gitkOptionsLabel->text()
+ << sep << m_ui.repBrowserGroupBox->title()
+ << sep << m_ui.repBrowserCommandLabel->text()
;
rc.remove(QLatin1Char('&'));
return rc;
diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui
index 2453404c74..b12fbe028d 100644
--- a/src/plugins/git/settingspage.ui
+++ b/src/plugins/git/settingspage.ui
@@ -153,6 +153,32 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="repBrowserGroupBox">
+ <property name="title">
+ <string>Repository browser</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="repBrowserCommandLabel">
+ <property name="text">
+ <string>Command:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Utils::PathChooser" name="repBrowserCommandPathChooser" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -167,6 +193,18 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::PathChooser</class>
+ <extends>QWidget</extends>
+ <header location="global">utils/pathchooser.h</header>
+ <container>1</container>
+ <slots>
+ <signal>editingFinished()</signal>
+ <signal>browsingFinished()</signal>
+ </slots>
+ </customwidget>
+ </customwidgets>
<tabstops>
<tabstop>pathLineEdit</tabstop>
</tabstops>
diff --git a/src/plugins/glsleditor/glsleditorplugin.h b/src/plugins/glsleditor/glsleditorplugin.h
index 4951cdeb46..37e892b472 100644
--- a/src/plugins/glsleditor/glsleditorplugin.h
+++ b/src/plugins/glsleditor/glsleditorplugin.h
@@ -73,6 +73,7 @@ class GLSLQuickFixCollector;
class GLSLEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "GLSLEditor.json")
public:
GLSLEditorPlugin();
diff --git a/src/plugins/helloworld/helloworldplugin.h b/src/plugins/helloworld/helloworldplugin.h
index 3e0852ca65..0702513b25 100644
--- a/src/plugins/helloworld/helloworldplugin.h
+++ b/src/plugins/helloworld/helloworldplugin.h
@@ -42,6 +42,7 @@ class HelloWorldPlugin
: public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "HelloWorld.json")
public:
HelloWorldPlugin();
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index df86dc24c8..0ef8f145d9 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -472,6 +472,8 @@ void HelpPlugin::setupUi()
m_bookmarkItem = new Core::SideBarItem(bookmarkWidget, QLatin1String(SB_BOOKMARKS));
connect(bookmarkWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
SLOT(setSource(QUrl)));
+ connect(bookmarkWidget, SIGNAL(createPage(QUrl,bool)), &OpenPagesManager::instance(),
+ SLOT(createPage(QUrl,bool)));
shortcut = new QShortcut(m_splitter);
shortcut->setWhatsThis(tr("Activate Bookmarks in Help mode"));
diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h
index 17c0138318..4a0b10df12 100644
--- a/src/plugins/help/helpplugin.h
+++ b/src/plugins/help/helpplugin.h
@@ -70,6 +70,7 @@ class SearchWidget;
class HelpPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Help.json")
public:
HelpPlugin();
diff --git a/src/plugins/imageviewer/imageviewerplugin.h b/src/plugins/imageviewer/imageviewerplugin.h
index 66aba08157..c077f7b767 100644
--- a/src/plugins/imageviewer/imageviewerplugin.h
+++ b/src/plugins/imageviewer/imageviewerplugin.h
@@ -46,6 +46,7 @@ class ImageViewerFactory;
class ImageViewerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ImageViewer.json")
public:
ImageViewerPlugin();
diff --git a/src/plugins/locator/locatorplugin.h b/src/plugins/locator/locatorplugin.h
index 8c5a56a9e7..99913c1bfa 100644
--- a/src/plugins/locator/locatorplugin.h
+++ b/src/plugins/locator/locatorplugin.h
@@ -44,6 +44,10 @@
#include <QTimer>
#include <QFutureWatcher>
+#if QT_VERSION >= 0x050000
+# include <QtPlugin>
+#endif
+
namespace Locator {
namespace Internal {
@@ -56,6 +60,7 @@ class LocatorPlugin;
class LocatorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Locator.json")
public:
LocatorPlugin();
diff --git a/src/plugins/macros/macrosplugin.h b/src/plugins/macros/macrosplugin.h
index f434508480..39c8547686 100644
--- a/src/plugins/macros/macrosplugin.h
+++ b/src/plugins/macros/macrosplugin.h
@@ -44,6 +44,7 @@ namespace Internal {
class MacrosPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Macros.json")
public:
MacrosPlugin();
diff --git a/src/plugins/madde/maddedeviceconfigurationfactory.cpp b/src/plugins/madde/maddedeviceconfigurationfactory.cpp
index e7e3cdfafc..dbde15a5fb 100644
--- a/src/plugins/madde/maddedeviceconfigurationfactory.cpp
+++ b/src/plugins/madde/maddedeviceconfigurationfactory.cpp
@@ -43,6 +43,7 @@
#include <remotelinux/genericlinuxdeviceconfigurationwidget.h>
#include <utils/qtcassert.h>
+using namespace ProjectExplorer;
using namespace RemoteLinux;
namespace Madde {
@@ -53,7 +54,7 @@ const char MaddeRemoteProcessesActionId[] = "Madde.RemoteProcessesAction";
} // anonymous namespace
MaddeDeviceConfigurationFactory::MaddeDeviceConfigurationFactory(QObject *parent)
- : ILinuxDeviceConfigurationFactory(parent)
+ : IDeviceFactory(parent)
{
}
@@ -62,30 +63,39 @@ QString MaddeDeviceConfigurationFactory::displayName() const
return tr("Device with MADDE support (Fremantle, Harmattan, MeeGo)");
}
-ILinuxDeviceConfigurationWizard *MaddeDeviceConfigurationFactory::createWizard(QWidget *parent) const
+IDeviceWizard *MaddeDeviceConfigurationFactory::createWizard(QWidget *parent) const
{
return new MaemoDeviceConfigWizard(parent);
}
-ILinuxDeviceConfigurationWidget *MaddeDeviceConfigurationFactory::createWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
+IDeviceWidget *MaddeDeviceConfigurationFactory::createWidget(const IDevice::Ptr &device,
QWidget *parent) const
{
- return new GenericLinuxDeviceConfigurationWidget(deviceConfig, parent);
+ return new GenericLinuxDeviceConfigurationWidget(device.staticCast<LinuxDeviceConfiguration>(),
+ parent);
}
-bool MaddeDeviceConfigurationFactory::supportsOsType(const QString &osType) const
+IDevice::Ptr MaddeDeviceConfigurationFactory::loadDevice(const QVariantMap &map) const
{
- return osType == QLatin1String(Maemo5OsType) || osType == QLatin1String(HarmattanOsType)
- || osType == QLatin1String(MeeGoOsType);
+ QTC_ASSERT(supportsDeviceType(IDevice::typeFromMap(map)),
+ return LinuxDeviceConfiguration::Ptr());
+ LinuxDeviceConfiguration::Ptr device = LinuxDeviceConfiguration::create();
+ device->fromMap(map);
+ return device;
}
-QString MaddeDeviceConfigurationFactory::displayNameForOsType(const QString &osType) const
+bool MaddeDeviceConfigurationFactory::supportsDeviceType(const QString &type) const
{
- QTC_ASSERT(supportsOsType(osType), return QString());
- if (osType == QLatin1String(Maemo5OsType))
+ return type == QLatin1String(Maemo5OsType) || type == QLatin1String(HarmattanOsType)
+ || type == QLatin1String(MeeGoOsType);
+}
+
+QString MaddeDeviceConfigurationFactory::displayNameForDeviceType(const QString &deviceType) const
+{
+ QTC_ASSERT(supportsDeviceType(deviceType), return QString());
+ if (deviceType == QLatin1String(Maemo5OsType))
return tr("Maemo5/Fremantle");
- if (osType == QLatin1String(HarmattanOsType))
+ if (deviceType == QLatin1String(HarmattanOsType))
return tr("MeeGo 1.2 Harmattan");
return tr("Other MeeGo OS");
}
@@ -111,16 +121,18 @@ QString MaddeDeviceConfigurationFactory::displayNameForActionId(const QString &a
}
QDialog *MaddeDeviceConfigurationFactory::createDeviceAction(const QString &actionId,
- const LinuxDeviceConfiguration::ConstPtr &deviceConfig, QWidget *parent) const
+ const IDevice::ConstPtr &device, QWidget *parent) const
{
Q_ASSERT(supportedDeviceActionIds().contains(actionId));
+ const LinuxDeviceConfiguration::ConstPtr lDevice
+ = device.staticCast<const LinuxDeviceConfiguration>();
if (actionId == QLatin1String(MaddeDeviceTestActionId))
- return new LinuxDeviceTestDialog(deviceConfig, new MaddeDeviceTester, parent);
+ return new LinuxDeviceTestDialog(lDevice, new MaddeDeviceTester, parent);
if (actionId == QLatin1String(MaddeRemoteProcessesActionId))
- return new RemoteLinuxProcessesDialog(new GenericRemoteLinuxProcessList(deviceConfig), parent);
+ return new RemoteLinuxProcessesDialog(new GenericRemoteLinuxProcessList(lDevice), parent);
if (actionId == QLatin1String(Constants::GenericDeployKeyToDeviceActionId))
- return PublicKeyDeploymentDialog::createDialog(deviceConfig, parent);
+ return PublicKeyDeploymentDialog::createDialog(lDevice, parent);
return 0; // Can't happen.
}
diff --git a/src/plugins/madde/maddedeviceconfigurationfactory.h b/src/plugins/madde/maddedeviceconfigurationfactory.h
index b07bbd74ea..cc1451bc34 100644
--- a/src/plugins/madde/maddedeviceconfigurationfactory.h
+++ b/src/plugins/madde/maddedeviceconfigurationfactory.h
@@ -32,28 +32,28 @@
#ifndef MADDEDEVICECONFIGURATIONFACTORY_H
#define MADDEDEVICECONFIGURATIONFACTORY_H
-#include <remotelinux/linuxdeviceconfiguration.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace Madde {
namespace Internal {
-class MaddeDeviceConfigurationFactory : public RemoteLinux::ILinuxDeviceConfigurationFactory
+class MaddeDeviceConfigurationFactory : public ProjectExplorer::IDeviceFactory
{
Q_OBJECT
public:
MaddeDeviceConfigurationFactory(QObject *parent = 0);
QString displayName() const;
- RemoteLinux::ILinuxDeviceConfigurationWizard *createWizard(QWidget *parent) const;
- RemoteLinux::ILinuxDeviceConfigurationWidget *createWidget(
- const RemoteLinux::LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent = 0) const;
- bool supportsOsType(const QString &osType) const;
- QString displayNameForOsType(const QString &osType) const;
+ ProjectExplorer::IDeviceWizard *createWizard(QWidget *parent) const;
+ ProjectExplorer::IDeviceWidget *createWidget(
+ const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = 0) const;
+ ProjectExplorer::IDevice::Ptr loadDevice(const QVariantMap &map) const;
+ bool supportsDeviceType(const QString &type) const;
+ QString displayNameForDeviceType(const QString &deviceType) const;
QStringList supportedDeviceActionIds() const;
QString displayNameForActionId(const QString &actionId) const;
QDialog *createDeviceAction(const QString &actionId,
- const RemoteLinux::LinuxDeviceConfiguration::ConstPtr &deviceConfig, QWidget *parent) const;
+ const ProjectExplorer::IDevice::ConstPtr &device, QWidget *parent) const;
};
} // namespace Internal
diff --git a/src/plugins/madde/maddedevicetester.cpp b/src/plugins/madde/maddedevicetester.cpp
index 77ce8a59fc..c3e6498b11 100644
--- a/src/plugins/madde/maddedevicetester.cpp
+++ b/src/plugins/madde/maddedevicetester.cpp
@@ -117,7 +117,7 @@ void MaddeDeviceTester::handleGenericTestFinished(TestResult result)
connect(m_processRunner, SIGNAL(processClosed(int)), SLOT(handleProcessFinished(int)));
QString qtInfoCmd;
- if (m_deviceConfiguration->osType() == QLatin1String(MeeGoOsType)) {
+ if (m_deviceConfiguration->type() == QLatin1String(MeeGoOsType)) {
qtInfoCmd = QLatin1String("rpm -qa 'libqt*' --queryformat '%{NAME} %{VERSION}\\n'");
} else {
qtInfoCmd = QLatin1String("dpkg-query -W -f "
@@ -212,7 +212,7 @@ void MaddeDeviceTester::handleMadDeveloperTestFinished(int exitStatus)
} else if (m_processRunner->processExitCode() != 0) {
QString message = tr("Connectivity tool not installed on device. "
"Deployment currently not possible.");
- if (m_deviceConfiguration->osType() == QLatin1String(HarmattanOsType)) {
+ if (m_deviceConfiguration->type() == QLatin1String(HarmattanOsType)) {
message += tr("Please switch the device to developer mode "
"via Settings -> Security.");
}
@@ -222,7 +222,7 @@ void MaddeDeviceTester::handleMadDeveloperTestFinished(int exitStatus)
emit progressMessage(tr("Connectivity tool present.\n"));
}
- if (m_deviceConfiguration->osType() != QLatin1String(HarmattanOsType)) {
+ if (m_deviceConfiguration->type() != QLatin1String(HarmattanOsType)) {
setFinished();
return;
}
@@ -263,7 +263,7 @@ QString MaddeDeviceTester::processedQtLibsList()
QString unfilteredLibs = QString::fromUtf8(m_stdout);
QString filteredLibs;
QString patternString;
- if (m_deviceConfiguration->osType() == QLatin1String(MeeGoOsType))
+ if (m_deviceConfiguration->type() == QLatin1String(MeeGoOsType))
patternString = QLatin1String("(libqt\\S+) ((\\d+)\\.(\\d+)\\.(\\d+))");
else
patternString = QLatin1String("(\\S+) (\\S*(\\d+)\\.(\\d+)\\.(\\d+)\\S*) \\S+ \\S+ \\S+");
diff --git a/src/plugins/madde/maddeplugin.h b/src/plugins/madde/maddeplugin.h
index 1d68602342..60930b83c5 100644
--- a/src/plugins/madde/maddeplugin.h
+++ b/src/plugins/madde/maddeplugin.h
@@ -41,6 +41,8 @@ namespace Internal {
class MaddePlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Madde.json")
+
public:
MaddePlugin();
~MaddePlugin();
diff --git a/src/plugins/madde/maddeuploadandinstallpackagesteps.cpp b/src/plugins/madde/maddeuploadandinstallpackagesteps.cpp
index c689be8abd..2071dfb7af 100644
--- a/src/plugins/madde/maddeuploadandinstallpackagesteps.cpp
+++ b/src/plugins/madde/maddeuploadandinstallpackagesteps.cpp
@@ -61,7 +61,7 @@ protected:
void doDeviceSetup()
{
- if (deviceConfiguration()->deviceType() == LinuxDeviceConfiguration::Hardware) {
+ if (deviceConfiguration()->machineType() == LinuxDeviceConfiguration::Hardware) {
handleDeviceSetupDone(true);
return;
}
diff --git a/src/plugins/madde/maemodeploybymountsteps.cpp b/src/plugins/madde/maemodeploybymountsteps.cpp
index 03dde4061a..e30338e3b6 100644
--- a/src/plugins/madde/maemodeploybymountsteps.cpp
+++ b/src/plugins/madde/maemodeploybymountsteps.cpp
@@ -157,7 +157,7 @@ void AbstractMaemoDeployByMountService::doDeviceSetup()
{
QTC_ASSERT(m_state == Inactive, return);
- if (deviceConfiguration()->deviceType() == LinuxDeviceConfiguration::Hardware) {
+ if (deviceConfiguration()->machineType() == LinuxDeviceConfiguration::Hardware) {
handleDeviceSetupDone(true);
return;
}
diff --git a/src/plugins/madde/maemodeployconfigurationwidget.cpp b/src/plugins/madde/maemodeployconfigurationwidget.cpp
index 8aaa4c4aab..fc73329d1a 100644
--- a/src/plugins/madde/maemodeployconfigurationwidget.cpp
+++ b/src/plugins/madde/maemodeployconfigurationwidget.cpp
@@ -147,7 +147,7 @@ void MaemoDeployConfigurationWidget::addIcon()
{
DeployableFilesPerProFile * const proFileInfo = m_remoteLinuxWidget->currentModel();
const int iconDim
- = MaemoGlobal::applicationIconSize(deployConfiguration()->target()->supportedOsType());
+ = MaemoGlobal::applicationIconSize(deployConfiguration()->target());
const QString origFilePath = QFileDialog::getOpenFileName(this,
tr("Choose Icon (will be scaled to %1x%1 pixels, if necessary)").arg(iconDim),
proFileInfo->projectDir(), QLatin1String("(*.png)"));
@@ -212,7 +212,7 @@ QString MaemoDeployConfigurationWidget::remoteIconFilePath(const DeployableFiles
QString MaemoDeployConfigurationWidget::remoteIconDir() const
{
return QString::fromLatin1("/usr/share/icons/hicolor/%1x%1/apps")
- .arg(MaemoGlobal::applicationIconSize(deployConfiguration()->target()->supportedOsType()));
+ .arg(MaemoGlobal::applicationIconSize(deployConfiguration()->target()));
}
} // namespace Internal
diff --git a/src/plugins/madde/maemodeploymentmounter.cpp b/src/plugins/madde/maemodeploymentmounter.cpp
index 505b849942..5a43ec4c8d 100644
--- a/src/plugins/madde/maemodeploymentmounter.cpp
+++ b/src/plugins/madde/maemodeploymentmounter.cpp
@@ -142,7 +142,7 @@ void MaemoDeploymentMounter::handleUnmounted()
break;
case UnmountingCurrentDirs:
setState(GatheringPorts);
- m_portsGatherer->start(m_connection, m_devConf);
+ m_portsGatherer->start(m_devConf);
break;
case UnmountingCurrentMounts:
setState(Inactive);
diff --git a/src/plugins/madde/maemodeploymentmounter.h b/src/plugins/madde/maemodeploymentmounter.h
index a35e6d43f0..8e58628ffa 100644
--- a/src/plugins/madde/maemodeploymentmounter.h
+++ b/src/plugins/madde/maemodeploymentmounter.h
@@ -35,7 +35,7 @@
#include "maemomountspecification.h"
-#include <remotelinux/portlist.h>
+#include <utils/portlist.h>
#include <QList>
#include <QObject>
@@ -97,7 +97,7 @@ private:
QSharedPointer<const RemoteLinux::LinuxDeviceConfiguration> m_devConf;
MaemoRemoteMounter * const m_mounter;
RemoteLinux::RemoteLinuxUsedPortsGatherer * const m_portsGatherer;
- RemoteLinux::PortList m_freePorts;
+ Utils::PortList m_freePorts;
QList<MaemoMountSpecification> m_mountSpecs;
const Qt4ProjectManager::Qt4BuildConfiguration *m_buildConfig;
};
diff --git a/src/plugins/madde/maemodeviceconfigwizard.cpp b/src/plugins/madde/maemodeviceconfigwizard.cpp
index 2bc5558227..c8619f7a07 100644
--- a/src/plugins/madde/maemodeviceconfigwizard.cpp
+++ b/src/plugins/madde/maemodeviceconfigwizard.cpp
@@ -40,9 +40,10 @@
#include "maemoconstants.h"
#include "maemoglobal.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h>
+#include <remotelinux/linuxdeviceconfiguration.h>
#include <remotelinux/linuxdevicetestdialog.h>
-#include <remotelinux/remotelinuxutils.h>
#include <remotelinux/sshkeydeployer.h>
#include <utils/fileutils.h>
#include <utils/ssh/sshkeygenerator.h>
@@ -54,6 +55,7 @@
#include <QMessageBox>
#include <QWizardPage>
+using namespace ProjectExplorer;
using namespace RemoteLinux;
using namespace Utils;
@@ -61,14 +63,14 @@ namespace Madde {
namespace Internal {
namespace {
-QString defaultUser(const QString &osType)
+QString defaultUser(const QString &deviceType)
{
- if (osType == QLatin1String(MeeGoOsType))
+ if (deviceType == QLatin1String(MeeGoOsType))
return QLatin1String("meego");
return QLatin1String("developer");
}
-QString defaultHost(LinuxDeviceConfiguration::DeviceType type)
+QString defaultHost(LinuxDeviceConfiguration::MachineType type)
{
return QLatin1String(type == LinuxDeviceConfiguration::Hardware ? "192.168.2.15" : "localhost");
}
@@ -77,9 +79,9 @@ struct WizardData
{
QString configName;
QString hostName;
- QString osType;
+ QString deviceType;
SshConnectionParameters::AuthenticationType authType;
- LinuxDeviceConfiguration::DeviceType deviceType;
+ LinuxDeviceConfiguration::MachineType machineType;
QString privateKeyFilePath;
QString publicKeyFilePath;
QString userName;
@@ -104,25 +106,24 @@ public:
setTitle(tr("General Information"));
setSubTitle(QLatin1String(" ")); // For Qt bug (background color)
- m_ui->osTypeComboBox->addItem(RemoteLinuxUtils::osTypeToString(QLatin1String(Maemo5OsType)),
+ m_ui->osTypeComboBox->addItem(DeviceManager::displayNameForDeviceType(QLatin1String(Maemo5OsType)),
QLatin1String(Maemo5OsType));
- m_ui->osTypeComboBox->addItem(RemoteLinuxUtils::osTypeToString(QLatin1String(HarmattanOsType)),
+ m_ui->osTypeComboBox->addItem(DeviceManager::displayNameForDeviceType(QLatin1String(HarmattanOsType)),
QLatin1String(HarmattanOsType));
- m_ui->osTypeComboBox->addItem(RemoteLinuxUtils::osTypeToString(QLatin1String(MeeGoOsType)),
+ m_ui->osTypeComboBox->addItem(DeviceManager::displayNameForDeviceType(QLatin1String(MeeGoOsType)),
QLatin1String(MeeGoOsType));
QButtonGroup *buttonGroup = new QButtonGroup(this);
buttonGroup->setExclusive(true);
buttonGroup->addButton(m_ui->hwButton);
buttonGroup->addButton(m_ui->emulatorButton);
- connect(buttonGroup, SIGNAL(buttonClicked(int)),
- SLOT(handleDeviceTypeChanged()));
+ connect(buttonGroup, SIGNAL(buttonClicked(int)), SLOT(handleMachineTypeChanged()));
m_ui->nameLineEdit->setText(tr("MeeGo Device"));
m_ui->osTypeComboBox->setCurrentIndex(m_ui->osTypeComboBox->findData(QLatin1String(HarmattanOsType)));
m_ui->hwButton->setChecked(true);
- handleDeviceTypeChanged();
- m_ui->hostNameLineEdit->setText(defaultHost(deviceType()));
+ handleMachineTypeChanged();
+ m_ui->hostNameLineEdit->setText(defaultHost(machineType()));
m_ui->sshPortSpinBox->setMinimum(1);
m_ui->sshPortSpinBox->setMaximum(65535);
m_ui->sshPortSpinBox->setValue(22);
@@ -141,17 +142,17 @@ public:
QString hostName() const
{
- return deviceType() == LinuxDeviceConfiguration::Emulator
+ return machineType() == LinuxDeviceConfiguration::Emulator
? defaultHost(LinuxDeviceConfiguration::Emulator)
: m_ui->hostNameLineEdit->text().trimmed();
}
- QString osType() const
+ QString deviceType() const
{
return m_ui->osTypeComboBox->itemData(m_ui->osTypeComboBox->currentIndex()).toString();
}
- LinuxDeviceConfiguration::DeviceType deviceType() const
+ LinuxDeviceConfiguration::MachineType machineType() const
{
return m_ui->hwButton->isChecked()
? LinuxDeviceConfiguration::Hardware : LinuxDeviceConfiguration::Emulator;
@@ -159,14 +160,14 @@ public:
int sshPort() const
{
- return deviceType() == LinuxDeviceConfiguration::Emulator
+ return machineType() == LinuxDeviceConfiguration::Emulator
? 6666 : m_ui->sshPortSpinBox->value();
}
private slots:
- void handleDeviceTypeChanged()
+ void handleMachineTypeChanged()
{
- const bool enable = deviceType() == LinuxDeviceConfiguration::Hardware;
+ const bool enable = machineType() == LinuxDeviceConfiguration::Hardware;
m_ui->hostNameLabel->setEnabled(enable);
m_ui->hostNameLineEdit->setEnabled(enable);
m_ui->sshPortLabel->setEnabled(enable);
@@ -422,7 +423,7 @@ public:
m_ui->deviceAddressLineEdit->setText(m_wizardData.hostName);
m_ui->instructionLabel->setText(QString(m_instructionTextTemplate)
.replace(QLatin1String("%%%maddev%%%"),
- MaemoGlobal::madDeveloperUiName(m_wizardData.osType)));
+ MaemoGlobal::madDeveloperUiName(m_wizardData.deviceType)));
m_ui->passwordLineEdit->clear();
enableInput();
}
@@ -446,13 +447,13 @@ private:
m_ui->deviceAddressLineEdit->setEnabled(false);
m_ui->passwordLineEdit->setEnabled(false);
m_ui->deployButton->setEnabled(false);
- SshConnectionParameters sshParams(SshConnectionParameters::NoProxy);
+ SshConnectionParameters sshParams;
sshParams.authenticationType = SshConnectionParameters::AuthenticationByPassword;
sshParams.host = hostAddress();
sshParams.port = m_wizardData.sshPort;
sshParams.password = password();
sshParams.timeout = 10;
- sshParams.userName = defaultUser(m_wizardData.osType);
+ sshParams.userName = defaultUser(m_wizardData.deviceType);
m_ui->statusLabel->setText(tr("Deploying... "));
m_keyDeployer->deployPublicKey(sshParams, m_wizardData.publicKeyFilePath);
}
@@ -468,7 +469,7 @@ private:
QMessageBox::information(this, tr("Key Deployment Success"),
tr("The key was successfully deployed. You may now close "
"the \"%1\" application and continue.")
- .arg(MaemoGlobal::madDeveloperUiName(m_wizardData.osType)));
+ .arg(MaemoGlobal::madDeveloperUiName(m_wizardData.deviceType)));
m_ui->statusLabel->setText(m_ui->statusLabel->text() + tr("Done."));
m_isComplete = true;
emit completeChanged();
@@ -506,7 +507,7 @@ public:
private:
QString infoText() const
{
- if (m_wizardData.deviceType == LinuxDeviceConfiguration::Emulator)
+ if (m_wizardData.machineType == LinuxDeviceConfiguration::Emulator)
return tr("The new device configuration will now be created.");
return GenericLinuxDeviceConfigurationWizardFinalPage::infoText();
}
@@ -539,7 +540,7 @@ struct MaemoDeviceConfigWizardPrivate
MaemoDeviceConfigWizard::MaemoDeviceConfigWizard(QWidget *parent)
- : ILinuxDeviceConfigurationWizard(parent), d(new MaemoDeviceConfigWizardPrivate(this))
+ : IDeviceWizard(parent), d(new MaemoDeviceConfigWizardPrivate(this))
{
setWindowTitle(tr("New Device Configuration Setup"));
setPage(StartPageId, &d->startPage);
@@ -556,17 +557,17 @@ MaemoDeviceConfigWizard::~MaemoDeviceConfigWizard()
delete d;
}
-LinuxDeviceConfiguration::Ptr MaemoDeviceConfigWizard::deviceConfiguration()
+IDevice::Ptr MaemoDeviceConfigWizard::device()
{
bool doTest;
QString freePortsSpec;
- Utils::SshConnectionParameters sshParams(Utils::SshConnectionParameters::NoProxy);
- sshParams.userName = defaultUser(d->wizardData.osType);
+ Utils::SshConnectionParameters sshParams;
+ sshParams.userName = defaultUser(d->wizardData.deviceType);
sshParams.host = d->wizardData.hostName;
sshParams.port = d->wizardData.sshPort;
- if (d->wizardData.deviceType == LinuxDeviceConfiguration::Emulator) {
+ if (d->wizardData.machineType == LinuxDeviceConfiguration::Emulator) {
sshParams.authenticationType = Utils::SshConnectionParameters::AuthenticationByPassword;
- sshParams.password = d->wizardData.osType == QLatin1String(MeeGoOsType)
+ sshParams.password = d->wizardData.deviceType == QLatin1String(MeeGoOsType)
? QLatin1String("meego") : QString();
sshParams.timeout = 30;
freePortsSpec = QLatin1String("13219,14168");
@@ -579,8 +580,9 @@ LinuxDeviceConfiguration::Ptr MaemoDeviceConfigWizard::deviceConfiguration()
doTest = true;
}
const LinuxDeviceConfiguration::Ptr devConf = LinuxDeviceConfiguration::create(d->wizardData.configName,
- d->wizardData.osType, d->wizardData.deviceType, PortList::fromString(freePortsSpec),
- sshParams);
+ d->wizardData.deviceType, d->wizardData.machineType);
+ devConf->setFreePorts(PortList::fromString(freePortsSpec));
+ devConf->setSshParameters(sshParams);
if (doTest) {
LinuxDeviceTestDialog dlg(devConf, new MaddeDeviceTester(this), this);
dlg.exec();
@@ -593,11 +595,11 @@ int MaemoDeviceConfigWizard::nextId() const
switch (currentId()) {
case StartPageId:
d->wizardData.configName = d->startPage.configName();
- d->wizardData.osType = d->startPage.osType();
d->wizardData.deviceType = d->startPage.deviceType();
+ d->wizardData.machineType = d->startPage.machineType();
d->wizardData.hostName = d->startPage.hostName();
d->wizardData.sshPort = d->startPage.sshPort();
- if (d->wizardData.deviceType == LinuxDeviceConfiguration::Emulator)
+ if (d->wizardData.machineType == LinuxDeviceConfiguration::Emulator)
return FinalPageId;
return PreviousKeySetupCheckPageId;
case PreviousKeySetupCheckPageId:
diff --git a/src/plugins/madde/maemodeviceconfigwizard.h b/src/plugins/madde/maemodeviceconfigwizard.h
index 99dfba76c5..9ea0f11eab 100644
--- a/src/plugins/madde/maemodeviceconfigwizard.h
+++ b/src/plugins/madde/maemodeviceconfigwizard.h
@@ -32,14 +32,15 @@
#ifndef MAEMODEVICECONFIGWIZARD_H
#define MAEMODEVICECONFIGWIZARD_H
-#include <remotelinux/linuxdeviceconfiguration.h>
+#include <projectexplorer/devicesupport/idevice.h>
+#include <projectexplorer/devicesupport/idevicewizard.h>
namespace Madde {
namespace Internal {
struct MaemoDeviceConfigWizardPrivate;
-class MaemoDeviceConfigWizard : public RemoteLinux::ILinuxDeviceConfigurationWizard
+class MaemoDeviceConfigWizard : public ProjectExplorer::IDeviceWizard
{
Q_OBJECT
@@ -47,7 +48,7 @@ public:
explicit MaemoDeviceConfigWizard(QWidget *parent = 0);
~MaemoDeviceConfigWizard();
- RemoteLinux::LinuxDeviceConfiguration::Ptr deviceConfiguration();
+ ProjectExplorer::IDevice::Ptr device();
virtual int nextId() const;
diff --git a/src/plugins/madde/maemoglobal.cpp b/src/plugins/madde/maemoglobal.cpp
index d59998d803..7ff3b4851f 100644
--- a/src/plugins/madde/maemoglobal.cpp
+++ b/src/plugins/madde/maemoglobal.cpp
@@ -33,6 +33,7 @@
#include "maemoconstants.h"
#include "maemoqemumanager.h"
+#include "qt4maemotarget.h"
#include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <qtsupport/baseqtversion.h>
@@ -92,9 +93,9 @@ bool MaemoGlobal::isValidMeegoQtVersion(const QString &qmakePath)
return isValidMaemoQtVersion(qmakePath, QLatin1String(MeeGoOsType));
}
-bool MaemoGlobal::isValidMaemoQtVersion(const QString &qmakePath, const QString &osType)
+bool MaemoGlobal::isValidMaemoQtVersion(const QString &qmakePath, const QString &deviceType)
{
- if (MaemoGlobal::osType(qmakePath) != osType)
+ if (MaemoGlobal::deviceType(qmakePath) != deviceType)
return false;
QProcess madAdminProc;
const QStringList arguments(QLatin1String("list"));
@@ -127,17 +128,17 @@ QString MaemoGlobal::devrootshPath()
return QLatin1String("/usr/lib/mad-developer/devrootsh");
}
-int MaemoGlobal::applicationIconSize(const QString &osType)
+int MaemoGlobal::applicationIconSize(const ProjectExplorer::Target *target)
{
- return osType == QLatin1String(HarmattanOsType) ? 80 : 64;
+ return qobject_cast<const Qt4HarmattanTarget *>(target) ? 80 : 64;
}
-QString MaemoGlobal::remoteSudo(const QString &osType, const QString &uname)
+QString MaemoGlobal::remoteSudo(const QString &deviceType, const QString &uname)
{
if (uname == QLatin1String("root"))
return QString();
- if (osType == QLatin1String(Maemo5OsType) || osType == QLatin1String(HarmattanOsType)
- || osType == QLatin1String(MeeGoOsType)) {
+ if (deviceType == QLatin1String(Maemo5OsType) || deviceType == QLatin1String(HarmattanOsType)
+ || deviceType == QLatin1String(MeeGoOsType)) {
return devrootshPath();
}
return QString(); // Using sudo would open a can of worms.
@@ -153,12 +154,12 @@ QString MaemoGlobal::remoteSourceProfilesCommand()
return QString::fromAscii(remoteCall);
}
-PortList MaemoGlobal::freePorts(const LinuxDeviceConfiguration::ConstPtr &devConf,
+Utils::PortList MaemoGlobal::freePorts(const LinuxDeviceConfiguration::ConstPtr &devConf,
const QtSupport::BaseQtVersion *qtVersion)
{
if (!devConf || !qtVersion)
- return PortList();
- if (devConf->deviceType() == LinuxDeviceConfiguration::Emulator) {
+ return Utils::PortList();
+ if (devConf->machineType() == LinuxDeviceConfiguration::Emulator) {
MaemoQemuRuntime rt;
const int id = qtVersion->uniqueId();
if (MaemoQemuManager::instance().runtimeForQtVersion(id, &rt))
@@ -199,13 +200,13 @@ QString MaemoGlobal::madCommand(const QString &qmakePath)
return maddeRoot(qmakePath) + QLatin1String("/bin/mad");
}
-QString MaemoGlobal::madDeveloperUiName(const QString &osType)
+QString MaemoGlobal::madDeveloperUiName(const QString &deviceType)
{
- return osType == QLatin1String(HarmattanOsType)
+ return deviceType == QLatin1String(HarmattanOsType)
? tr("SDK Connectivity") : tr("Mad Developer");
}
-QString MaemoGlobal::osType(const QString &qmakePath)
+QString MaemoGlobal::deviceType(const QString &qmakePath)
{
const QString &name = targetName(qmakePath);
if (name.startsWith(QLatin1String("fremantle")))
diff --git a/src/plugins/madde/maemoglobal.h b/src/plugins/madde/maemoglobal.h
index 3fb9b54aac..9dfc1b2742 100644
--- a/src/plugins/madde/maemoglobal.h
+++ b/src/plugins/madde/maemoglobal.h
@@ -34,7 +34,7 @@
#define MAEMOGLOBAL_H
#include <coreplugin/idocument.h>
-#include <remotelinux/portlist.h>
+#include <utils/portlist.h>
#include <utils/environment.h>
#include <QCoreApplication>
@@ -45,13 +45,9 @@ class QProcess;
class QString;
QT_END_NAMESPACE
-namespace QtSupport {
-class BaseQtVersion;
-}
-
-namespace RemoteLinux {
-class LinuxDeviceConfiguration;
-}
+namespace QtSupport { class BaseQtVersion; }
+namespace RemoteLinux { class LinuxDeviceConfiguration; }
+namespace ProjectExplorer { class Target; }
namespace Madde {
namespace Internal {
@@ -97,10 +93,10 @@ public:
static QString homeDirOnDevice(const QString &uname);
static QString devrootshPath();
- static int applicationIconSize(const QString &osType);
- static QString remoteSudo(const QString &osType, const QString &uname);
+ static int applicationIconSize(const ProjectExplorer::Target *target);
+ static QString remoteSudo(const QString &deviceType, const QString &uname);
static QString remoteSourceProfilesCommand();
- static RemoteLinux::PortList freePorts(const QSharedPointer<const RemoteLinux::LinuxDeviceConfiguration> &devConf,
+ static Utils::PortList freePorts(const QSharedPointer<const RemoteLinux::LinuxDeviceConfiguration> &devConf,
const QtSupport::BaseQtVersion *qtVersion);
static void addMaddeEnvironment(Utils::Environment &env, const QString &qmakePath);
@@ -109,8 +105,8 @@ public:
static QString targetRoot(const QString &qmakePath);
static QString targetName(const QString &qmakePath);
static QString madCommand(const QString &qmakePath);
- static QString madDeveloperUiName(const QString &osType);
- static QString osType(const QString &qmakePath);
+ static QString madDeveloperUiName(const QString &deviceType);
+ static QString deviceType(const QString &qmakePath);
// TODO: IS this still needed with Qt Version having an Abi?
static QString architecture(const QString &qmakePath);
@@ -120,7 +116,7 @@ public:
static bool callMadAdmin(QProcess &proc, const QStringList &args,
const QString &qmakePath, bool useTarget);
- static bool isValidMaemoQtVersion(const QString &qmakePath, const QString &osType);
+ static bool isValidMaemoQtVersion(const QString &qmakePath, const QString &deviceType);
private:
static QString madAdminCommand(const QString &qmakePath);
static bool callMaddeShellScript(QProcess &proc, const QString &qmakePath,
diff --git a/src/plugins/madde/maemopublisherfremantlefree.cpp b/src/plugins/madde/maemopublisherfremantlefree.cpp
index cf39195f66..896a138513 100644
--- a/src/plugins/madde/maemopublisherfremantlefree.cpp
+++ b/src/plugins/madde/maemopublisherfremantlefree.cpp
@@ -69,7 +69,6 @@ MaemoPublisherFremantleFree::MaemoPublisherFremantleFree(const ProjectExplorer::
QObject(parent),
m_project(project),
m_state(Inactive),
- m_sshParams(SshConnectionParameters::DefaultProxy),
m_uploader(0)
{
m_sshParams.authenticationType = SshConnectionParameters::AuthenticationByKey;
diff --git a/src/plugins/madde/maemopublishingbuildsettingspagefremantlefree.cpp b/src/plugins/madde/maemopublishingbuildsettingspagefremantlefree.cpp
index 53da32fe71..396d1246e3 100644
--- a/src/plugins/madde/maemopublishingbuildsettingspagefremantlefree.cpp
+++ b/src/plugins/madde/maemopublishingbuildsettingspagefremantlefree.cpp
@@ -86,7 +86,7 @@ void MaemoPublishingBuildSettingsPageFremantleFree::collectBuildConfigurations(c
QtSupport::BaseQtVersion *lqt = qt4Bc->qtVersion();
if (!lqt)
continue;
- if (MaemoGlobal::osType(lqt->qmakeCommand().toString()) == QLatin1String(Maemo5OsType))
+ if (MaemoGlobal::deviceType(lqt->qmakeCommand().toString()) == QLatin1String(Maemo5OsType))
m_buildConfigs << qt4Bc;
}
break;
diff --git a/src/plugins/madde/maemopublishingwizardfactories.cpp b/src/plugins/madde/maemopublishingwizardfactories.cpp
index eb90bde72b..6524527856 100644
--- a/src/plugins/madde/maemopublishingwizardfactories.cpp
+++ b/src/plugins/madde/maemopublishingwizardfactories.cpp
@@ -84,7 +84,7 @@ bool MaemoPublishingWizardFactoryFremantleFree::canCreateWizard(const Project *p
QtSupport::BaseQtVersion *qt = qt4Bc->qtVersion();
if (!qt)
continue;
- if (MaemoGlobal::osType(qt->qmakeCommand().toString()) == QLatin1String(Maemo5OsType))
+ if (MaemoGlobal::deviceType(qt->qmakeCommand().toString()) == QLatin1String(Maemo5OsType))
return true;
}
break;
diff --git a/src/plugins/madde/maemoqemumanager.cpp b/src/plugins/madde/maemoqemumanager.cpp
index 4f0a29e783..49d2ee10eb 100644
--- a/src/plugins/madde/maemoqemumanager.cpp
+++ b/src/plugins/madde/maemoqemumanager.cpp
@@ -593,7 +593,7 @@ bool MaemoQemuManager::targetUsesMatchingRuntimeConfig(Target *target,
if (qtVersion)
*qtVersion = version;
const LinuxDeviceConfiguration::ConstPtr &config = mrc->deviceConfig();
- return config && config->deviceType() == LinuxDeviceConfiguration::Emulator;
+ return config && config->machineType() == LinuxDeviceConfiguration::Emulator;
}
void MaemoQemuManager::notify(const QList<int> uniqueIds)
diff --git a/src/plugins/madde/maemoqemuruntime.h b/src/plugins/madde/maemoqemuruntime.h
index 04adfd255a..6b06cb489c 100644
--- a/src/plugins/madde/maemoqemuruntime.h
+++ b/src/plugins/madde/maemoqemuruntime.h
@@ -34,7 +34,7 @@
#include "maemoqemusettings.h"
-#include <remotelinux/portlist.h>
+#include <utils/portlist.h>
#include <QHash>
#include <QList>
@@ -79,7 +79,7 @@ struct MaemoQemuRuntime
QString m_args;
QString m_sshPort;
QString m_watchPath;
- RemoteLinux::PortList m_freePorts;
+ Utils::PortList m_freePorts;
QList<Variable> m_normalVars;
QString m_openGlBackendVarName;
QHash<MaemoQemuSettings::OpenGlMode, QString> m_openGlBackendVarValues;
diff --git a/src/plugins/madde/maemoqemuruntimeparser.cpp b/src/plugins/madde/maemoqemuruntimeparser.cpp
index 0c6979ce73..e889dbe075 100644
--- a/src/plugins/madde/maemoqemuruntimeparser.cpp
+++ b/src/plugins/madde/maemoqemuruntimeparser.cpp
@@ -190,7 +190,7 @@ void MaemoQemuRuntimeParserV1::fillRuntimeInformation(MaemoQemuRuntime *runtime)
runtime->m_args = map.value(QLatin1String("qemu_args"));
setEnvironment(runtime, map.value(QLatin1String("libpath")));
runtime->m_sshPort = map.value(QLatin1String("sshport"));
- runtime->m_freePorts = PortList();
+ runtime->m_freePorts = Utils::PortList();
int i = 2;
while (true) {
const QString port = map.value(QLatin1String("redirport")
diff --git a/src/plugins/madde/maemoqtversion.cpp b/src/plugins/madde/maemoqtversion.cpp
index 6c5a692a81..1ca3e9f8bc 100644
--- a/src/plugins/madde/maemoqtversion.cpp
+++ b/src/plugins/madde/maemoqtversion.cpp
@@ -57,7 +57,7 @@ MaemoQtVersion::MaemoQtVersion()
MaemoQtVersion::MaemoQtVersion(const Utils::FileName &path, bool isAutodetected, const QString &autodetectionSource)
: QtSupport::BaseQtVersion(path, isAutodetected, autodetectionSource),
- m_osType(MaemoGlobal::osType(path.toString())),
+ m_deviceType(MaemoGlobal::deviceType(path.toString())),
m_isvalidVersion(false),
m_initialized(false)
{
@@ -73,7 +73,7 @@ void MaemoQtVersion::fromMap(const QVariantMap &map)
{
QtSupport::BaseQtVersion::fromMap(map);
QString path = qmakeCommand().toString();
- m_osType = MaemoGlobal::osType(path);
+ m_deviceType = MaemoGlobal::deviceType(path);
}
QString MaemoQtVersion::type() const
@@ -86,7 +86,7 @@ bool MaemoQtVersion::isValid() const
if (!BaseQtVersion::isValid())
return false;
if (!m_initialized) {
- m_isvalidVersion = MaemoGlobal::isValidMaemoQtVersion(qmakeCommand().toString(), m_osType);
+ m_isvalidVersion = MaemoGlobal::isValidMaemoQtVersion(qmakeCommand().toString(), m_deviceType);
m_initialized = true;
}
return m_isvalidVersion;
@@ -124,16 +124,16 @@ QList<ProjectExplorer::Abi> MaemoQtVersion::detectQtAbis() const
QList<ProjectExplorer::Abi> result;
if (!isValid())
return result;
- if (m_osType == QLatin1String(Maemo5OsType)) {
+ if (m_deviceType == QLatin1String(Maemo5OsType)) {
result.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
ProjectExplorer::Abi::MaemoLinuxFlavor, ProjectExplorer::Abi::ElfFormat,
32));
- } else if (m_osType == QLatin1String(HarmattanOsType)) {
+ } else if (m_deviceType == QLatin1String(HarmattanOsType)) {
result.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
ProjectExplorer::Abi::HarmattanLinuxFlavor,
ProjectExplorer::Abi::ElfFormat,
32));
- } else if (m_osType == QLatin1String(MeeGoOsType)) {
+ } else if (m_deviceType == QLatin1String(MeeGoOsType)) {
result.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
ProjectExplorer::Abi::MeegoLinuxFlavor,
ProjectExplorer::Abi::ElfFormat, 32));
@@ -151,11 +151,11 @@ QSet<QString> MaemoQtVersion::supportedTargetIds() const
QSet<QString> result;
if (!isValid())
return result;
- if (m_osType == QLatin1String(Maemo5OsType)) {
+ if (m_deviceType == QLatin1String(Maemo5OsType)) {
result.insert(QLatin1String(Constants::MAEMO5_DEVICE_TARGET_ID));
- } else if (m_osType == QLatin1String(HarmattanOsType)) {
+ } else if (m_deviceType == QLatin1String(HarmattanOsType)) {
result.insert(QLatin1String(Constants::HARMATTAN_DEVICE_TARGET_ID));
- } else if (m_osType == QLatin1String(MeeGoOsType)) {
+ } else if (m_deviceType == QLatin1String(MeeGoOsType)) {
result.insert(QLatin1String(Constants::MEEGO_DEVICE_TARGET_ID));
}
return result;
@@ -163,11 +163,11 @@ QSet<QString> MaemoQtVersion::supportedTargetIds() const
QString MaemoQtVersion::description() const
{
- if (m_osType == QLatin1String(Maemo5OsType))
+ if (m_deviceType == QLatin1String(Maemo5OsType))
return QCoreApplication::translate("QtVersion", "Maemo", "Qt Version is meant for Maemo5");
- else if (m_osType == QLatin1String(HarmattanOsType))
+ else if (m_deviceType == QLatin1String(HarmattanOsType))
return QCoreApplication::translate("QtVersion", "Harmattan ", "Qt Version is meant for Harmattan");
- else if (m_osType == QLatin1String(MeeGoOsType))
+ else if (m_deviceType == QLatin1String(MeeGoOsType))
return QCoreApplication::translate("QtVersion", "MeeGo", "Qt Version is meant for MeeGo");
return QString();
}
@@ -180,9 +180,9 @@ bool MaemoQtVersion::supportsShadowBuilds() const
return true;
}
-QString MaemoQtVersion::osType() const
+QString MaemoQtVersion::deviceType() const
{
- return m_osType;
+ return m_deviceType;
}
Core::FeatureSet MaemoQtVersion::availableFeatures() const
@@ -192,7 +192,7 @@ Core::FeatureSet MaemoQtVersion::availableFeatures() const
features |= Core::FeatureSet(QtSupport::Constants::FEATURE_QTQUICK_COMPONENTS_MEEGO);
features |= Core::FeatureSet(QtSupport::Constants::FEATURE_MOBILE);
- if (osType() != QLatin1String(Maemo5OsType)) //Only Maemo5 has proper support for Widgets
+ if (deviceType() != QLatin1String(Maemo5OsType)) //Only Maemo5 has proper support for Widgets
features.remove(Core::Feature(QtSupport::Constants::FEATURE_QWIDGETS));
return features;
diff --git a/src/plugins/madde/maemoqtversion.h b/src/plugins/madde/maemoqtversion.h
index 2b55c2cee8..ec65f43fcc 100644
--- a/src/plugins/madde/maemoqtversion.h
+++ b/src/plugins/madde/maemoqtversion.h
@@ -59,14 +59,14 @@ public:
QString description() const;
bool supportsShadowBuilds() const;
- QString osType() const;
+ QString deviceType() const;
Core::FeatureSet availableFeatures() const;
QString platformName() const;
QString platformDisplayName() const;
private:
mutable QString m_systemRoot;
- mutable QString m_osType;
+ mutable QString m_deviceType;
mutable bool m_isvalidVersion;
mutable bool m_initialized;
};
diff --git a/src/plugins/madde/maemoremotecopyfacility.cpp b/src/plugins/madde/maemoremotecopyfacility.cpp
index 849c497514..574a2efcba 100644
--- a/src/plugins/madde/maemoremotecopyfacility.cpp
+++ b/src/plugins/madde/maemoremotecopyfacility.cpp
@@ -138,7 +138,7 @@ void MaemoRemoteCopyFacility::copyNextFile()
#endif
QString command = QString::fromLatin1("%1 mkdir -p %3 && %1 cp -a %2 %3")
- .arg(MaemoGlobal::remoteSudo(m_devConf->osType(), m_devConf->sshParameters().userName),
+ .arg(MaemoGlobal::remoteSudo(m_devConf->type(), m_devConf->sshParameters().userName),
sourceFilePath, d.remoteDir);
emit progress(tr("Copying file '%1' to directory '%2' on the device...")
.arg(d.localFilePath, d.remoteDir));
diff --git a/src/plugins/madde/maemoremotemounter.cpp b/src/plugins/madde/maemoremotemounter.cpp
index fd39e54855..c8de89c90a 100644
--- a/src/plugins/madde/maemoremotemounter.cpp
+++ b/src/plugins/madde/maemoremotemounter.cpp
@@ -129,7 +129,7 @@ void MaemoRemoteMounter::unmount()
}
QString remoteCall;
- const QString remoteSudo = MaemoGlobal::remoteSudo(m_devConf->osType(),
+ const QString remoteSudo = MaemoGlobal::remoteSudo(m_devConf->type(),
m_connection->connectionParameters().userName);
for (int i = 0; i < m_mountSpecs.count(); ++i) {
remoteCall += QString::fromLatin1("%1 umount %2 && %1 rmdir %2;")
@@ -188,7 +188,7 @@ void MaemoRemoteMounter::stop()
void MaemoRemoteMounter::startUtfsClients()
{
const QString userName = m_connection->connectionParameters().userName;
- const QString chmodFuse = MaemoGlobal::remoteSudo(m_devConf->osType(),
+ const QString chmodFuse = MaemoGlobal::remoteSudo(m_devConf->type(),
userName) + QLatin1String(" chmod a+r+w /dev/fuse");
const QString chmodUtfsClient
= QLatin1String("chmod a+x ") + utfsClientOnDevice();
@@ -205,7 +205,7 @@ void MaemoRemoteMounter::startUtfsClients()
}
const QString remoteSudo
- = MaemoGlobal::remoteSudo(m_devConf->osType(), userName);
+ = MaemoGlobal::remoteSudo(m_devConf->type(), userName);
const MaemoMountSpecification &mountSpec = mountInfo.mountSpec;
const QString mkdir = QString::fromLatin1("%1 mkdir -p %2")
.arg(remoteSudo, mountSpec.remoteMountPoint);
@@ -216,7 +216,7 @@ void MaemoRemoteMounter::startUtfsClients()
.arg(utfsClientOnDevice()).arg(mountInfo.remotePort)
.arg(mountSpec.remoteMountPoint);
if (mountInfo.mountAsRoot) {
- utfsClient.prepend(MaemoGlobal::remoteSudo(m_devConf->osType(),
+ utfsClient.prepend(MaemoGlobal::remoteSudo(m_devConf->type(),
userName) + QLatin1Char(' '));
}
QLatin1String seqOp("; ");
diff --git a/src/plugins/madde/maemoremotemounter.h b/src/plugins/madde/maemoremotemounter.h
index d01cdec728..8c4fa5c00d 100644
--- a/src/plugins/madde/maemoremotemounter.h
+++ b/src/plugins/madde/maemoremotemounter.h
@@ -44,6 +44,7 @@
QT_FORWARD_DECLARE_CLASS(QTimer)
namespace Utils {
+class PortList;
class SftpChannel;
class SshConnection;
class SshRemoteProcess;
@@ -53,7 +54,6 @@ namespace Qt4ProjectManager { class Qt4BuildConfiguration; }
namespace RemoteLinux {
class LinuxDeviceConfiguration;
-class PortList;
class RemoteLinuxUsedPortsGatherer;
}
@@ -76,7 +76,7 @@ public:
bool mountAsRoot);
bool hasValidMountSpecifications() const;
void resetMountSpecifications() { m_mountSpecs.clear(); }
- void mount(RemoteLinux::PortList *freePorts,
+ void mount(Utils::PortList *freePorts,
const RemoteLinux::RemoteLinuxUsedPortsGatherer *portsGatherer);
void unmount();
void stop();
@@ -132,7 +132,7 @@ private:
typedef QSharedPointer<QProcess> ProcPtr;
QList<ProcPtr> m_utfsServers;
- RemoteLinux::PortList *m_freePorts;
+ Utils::PortList *m_freePorts;
const RemoteLinux::RemoteLinuxUsedPortsGatherer *m_portsGatherer;
bool m_remoteMountsAllowed;
QString m_maddeRoot;
diff --git a/src/plugins/madde/maemorunconfiguration.cpp b/src/plugins/madde/maemorunconfiguration.cpp
index 845d1422fb..04d3e15853 100644
--- a/src/plugins/madde/maemorunconfiguration.cpp
+++ b/src/plugins/madde/maemorunconfiguration.cpp
@@ -41,7 +41,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
-#include <remotelinux/portlist.h>
+#include <utils/portlist.h>
#include <utils/ssh/sshconnection.h>
#include <QDir>
@@ -125,18 +125,18 @@ QString MaemoRunConfiguration::commandPrefix() const
return QString();
QString prefix = environmentPreparationCommand() + QLatin1Char(';');
- if (deviceConfig()->osType() == QLatin1String(MeeGoOsType))
+ if (deviceConfig()->type() == QLatin1String(MeeGoOsType))
prefix += QLatin1String("DISPLAY=:0.0 ");
return QString::fromLatin1("%1 %2").arg(prefix, userEnvironmentChangesAsString());
}
-PortList MaemoRunConfiguration::freePorts() const
+Utils::PortList MaemoRunConfiguration::freePorts() const
{
const Qt4BuildConfiguration * const bc = activeQt4BuildConfiguration();
return bc && deployConfig()
? MaemoGlobal::freePorts(deployConfig()->deviceConfiguration(), bc->qtVersion())
- : PortList();
+ : Utils::PortList();
}
QString MaemoRunConfiguration::localDirToMountForRemoteGdb() const
diff --git a/src/plugins/madde/maemorunconfiguration.h b/src/plugins/madde/maemorunconfiguration.h
index 3499e30679..ecd93e9be6 100644
--- a/src/plugins/madde/maemorunconfiguration.h
+++ b/src/plugins/madde/maemorunconfiguration.h
@@ -52,7 +52,7 @@ public:
QWidget *createConfigurationWidget();
QString environmentPreparationCommand() const;
QString commandPrefix() const;
- RemoteLinux::PortList freePorts() const;
+ Utils::PortList freePorts() const;
Internal::MaemoRemoteMountsModel *remoteMounts() const { return m_remoteMounts; }
bool hasEnoughFreePorts(ProjectExplorer::RunMode mode) const;
diff --git a/src/plugins/madde/maemorunconfigurationwidget.cpp b/src/plugins/madde/maemorunconfigurationwidget.cpp
index 95d238c8af..137f19d479 100644
--- a/src/plugins/madde/maemorunconfigurationwidget.cpp
+++ b/src/plugins/madde/maemorunconfigurationwidget.cpp
@@ -93,13 +93,14 @@ MaemoRunConfigurationWidget::MaemoRunConfigurationWidget(
= qobject_cast<AbstractQt4MaemoTarget *>(runConfiguration->target());
m_mountDetailsContainer->setVisible(maemoTarget->allowsRemoteMounts());
- connect(m_runConfiguration, SIGNAL(isEnabledChanged(bool)),
- this, SLOT(runConfigurationEnabledChange(bool)));
- runConfigurationEnabledChange(m_runConfiguration->isEnabled());
+ connect(m_runConfiguration, SIGNAL(enabledChanged()),
+ this, SLOT(runConfigurationEnabledChange()));
+ runConfigurationEnabledChange();
}
-void MaemoRunConfigurationWidget::runConfigurationEnabledChange(bool enabled)
+void MaemoRunConfigurationWidget::runConfigurationEnabledChange()
{
+ bool enabled = m_runConfiguration->isEnabled();
m_subWidget->setEnabled(enabled);
}
@@ -214,7 +215,7 @@ void MaemoRunConfigurationWidget::handleRemoteMountsChanged()
void MaemoRunConfigurationWidget::updateMountWarning()
{
QString mountWarning;
- const PortList &portList = m_runConfiguration->freePorts();
+ const Utils::PortList &portList = m_runConfiguration->freePorts();
const int availablePortCount = portList.count();
const int mountDirCount
= m_runConfiguration->remoteMounts()->validMountSpecificationCount();
diff --git a/src/plugins/madde/maemorunconfigurationwidget.h b/src/plugins/madde/maemorunconfigurationwidget.h
index d25e1b69db..cf738827f5 100644
--- a/src/plugins/madde/maemorunconfigurationwidget.h
+++ b/src/plugins/madde/maemorunconfigurationwidget.h
@@ -66,7 +66,7 @@ private slots:
void enableOrDisableRemoveMountSpecButton();
void handleRemoteMountsChanged();
void updateMountWarning();
- void runConfigurationEnabledChange(bool enabled);
+ void runConfigurationEnabledChange();
private:
void addMountWidgets(QVBoxLayout *mainLayout);
diff --git a/src/plugins/madde/maemosettingspages.cpp b/src/plugins/madde/maemosettingspages.cpp
index d93f8588f2..4f7a618f9f 100644
--- a/src/plugins/madde/maemosettingspages.cpp
+++ b/src/plugins/madde/maemosettingspages.cpp
@@ -34,7 +34,7 @@
#include "maemoqemusettingswidget.h"
#include <coreplugin/icore.h>
-#include <remotelinux/remotelinux_constants.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <QCoreApplication>
#include <QDialog>
@@ -169,7 +169,7 @@ QString MaemoQemuSettingsPage::pageId()
QString MaemoQemuSettingsPage::pageCategory()
{
- return QLatin1String(RemoteLinux::Constants::RemoteLinuxSettingsCategory);
+ return QLatin1String(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
}
} // namespace Internal
diff --git a/src/plugins/madde/maemosshrunner.cpp b/src/plugins/madde/maemosshrunner.cpp
index 846ef068e8..219b93515a 100644
--- a/src/plugins/madde/maemosshrunner.cpp
+++ b/src/plugins/madde/maemosshrunner.cpp
@@ -73,7 +73,7 @@ bool MaemoSshRunner::canRun(QString &whyNot) const
if (!AbstractRemoteLinuxApplicationRunner::canRun(whyNot))
return false;
- if (devConfig()->deviceType() == LinuxDeviceConfiguration::Emulator
+ if (devConfig()->machineType() == LinuxDeviceConfiguration::Emulator
&& !MaemoQemuManager::instance().qemuIsRunning()) {
MaemoQemuRuntime rt;
if (MaemoQemuManager::instance().runtimeForQtVersion(m_qtId, &rt)) {
diff --git a/src/plugins/madde/qt4maemotarget.cpp b/src/plugins/madde/qt4maemotarget.cpp
index 377ea648dd..b64eb3bfd9 100644
--- a/src/plugins/madde/qt4maemotarget.cpp
+++ b/src/plugins/madde/qt4maemotarget.cpp
@@ -122,8 +122,8 @@ bool adaptTagValue(QByteArray &document, const QByteArray &fieldName,
AbstractQt4MaemoTarget::AbstractQt4MaemoTarget(Qt4Project *parent, const QString &id,
- const QString &supportedOsType, const QString &qmakeScope) :
- AbstractEmbeddedLinuxTarget(parent, id, supportedOsType),
+ const QString &qmakeScope) :
+ AbstractEmbeddedLinuxTarget(parent, id),
m_filesWatcher(new Utils::FileSystemWatcher(this)),
m_deploymentSettingsAssistant(new DeploymentSettingsAssistant(qmakeScope,
QLatin1String("/opt"), deploymentInfo())),
@@ -375,8 +375,8 @@ void AbstractQt4MaemoTarget::raiseError(const QString &reason)
}
AbstractDebBasedQt4MaemoTarget::AbstractDebBasedQt4MaemoTarget(Qt4Project *parent,
- const QString &id, const QString &supportedOsType, const QString &qmakeScope)
- : AbstractQt4MaemoTarget(parent, id, supportedOsType, qmakeScope)
+ const QString &id, const QString &qmakeScope)
+ : AbstractQt4MaemoTarget(parent, id, qmakeScope)
{
}
@@ -958,8 +958,8 @@ QString AbstractDebBasedQt4MaemoTarget::shortDayOfWeekName(const QDateTime &dt)
AbstractRpmBasedQt4MaemoTarget::AbstractRpmBasedQt4MaemoTarget(Qt4Project *parent,
- const QString &id, const QString &supportedOsType, const QString &qmakeScope)
- : AbstractQt4MaemoTarget(parent, id, supportedOsType, qmakeScope)
+ const QString &id, const QString &qmakeScope)
+ : AbstractQt4MaemoTarget(parent, id, qmakeScope)
{
}
@@ -1124,14 +1124,18 @@ bool AbstractRpmBasedQt4MaemoTarget::setValueForTag(const QByteArray &tag,
}
Qt4Maemo5Target::Qt4Maemo5Target(Qt4Project *parent, const QString &id)
- : AbstractDebBasedQt4MaemoTarget(parent, id, QLatin1String(Maemo5OsType),
- QLatin1String("maemo5"))
+ : AbstractDebBasedQt4MaemoTarget(parent, id, QLatin1String("maemo5"))
{
setDisplayName(defaultDisplayName());
}
Qt4Maemo5Target::~Qt4Maemo5Target() {}
+bool Qt4Maemo5Target::supportsDevice(const ProjectExplorer::IDevice::ConstPtr &device) const
+{
+ return device->type() == QLatin1String(Maemo5OsType);
+}
+
QString Qt4Maemo5Target::defaultDisplayName()
{
return QApplication::translate("Qt4ProjectManager::Qt4Target", "Maemo5",
@@ -1164,14 +1168,18 @@ QByteArray Qt4Maemo5Target::defaultSection() const
}
Qt4HarmattanTarget::Qt4HarmattanTarget(Qt4Project *parent, const QString &id)
- : AbstractDebBasedQt4MaemoTarget(parent, id, QLatin1String(HarmattanOsType),
- QLatin1String("contains(MEEGO_EDITION,harmattan)"))
+ : AbstractDebBasedQt4MaemoTarget(parent, id, QLatin1String("contains(MEEGO_EDITION,harmattan)"))
{
setDisplayName(defaultDisplayName());
}
Qt4HarmattanTarget::~Qt4HarmattanTarget() {}
+bool Qt4HarmattanTarget::supportsDevice(const ProjectExplorer::IDevice::ConstPtr &device) const
+{
+ return device->type() == QLatin1String(HarmattanOsType);
+}
+
QString Qt4HarmattanTarget::defaultDisplayName()
{
return QApplication::translate("Qt4ProjectManager::Qt4Target", "Harmattan",
@@ -1208,7 +1216,9 @@ void Qt4HarmattanTarget::handleTargetAddedSpecial()
void Qt4HarmattanTarget::addAdditionalControlFileFields(QByteArray &controlContents)
{
- Q_UNUSED(controlContents);
+ adaptControlFileField(controlContents, "XB-Maemo-Flags", "visible");
+ adaptControlFileField(controlContents, "XB-MeeGo-Desktop-Entry-Filename", QString::fromLatin1("%1_harmattan").arg(project()->displayName()).toUtf8());
+ adaptControlFileField(controlContents, "XB-MeeGo-Desktop-Entry", QString::fromLatin1("\n [Desktop Entry]\n Type=Application\n Name=%1\n Icon=/usr/share/icons/hicolor/80x80/apps/%1%2.png").arg(project()->displayName()).arg(80).toUtf8());
}
QString Qt4HarmattanTarget::debianDirName() const
@@ -1233,7 +1243,7 @@ QByteArray Qt4HarmattanTarget::defaultSection() const
Qt4MeegoTarget::Qt4MeegoTarget(Qt4Project *parent, const QString &id)
- : AbstractRpmBasedQt4MaemoTarget(parent, id, QLatin1String(MeeGoOsType),
+ : AbstractRpmBasedQt4MaemoTarget(parent, id,
QLatin1String("!isEmpty(MEEGO_VERSION_MAJOR):!contains(MEEGO_EDITION,harmattan)"))
{
setDisplayName(defaultDisplayName());
@@ -1241,6 +1251,11 @@ Qt4MeegoTarget::Qt4MeegoTarget(Qt4Project *parent, const QString &id)
Qt4MeegoTarget::~Qt4MeegoTarget() {}
+bool Qt4MeegoTarget::supportsDevice(const ProjectExplorer::IDevice::ConstPtr &device) const
+{
+ return device->type() == QLatin1String(MeeGoOsType);
+}
+
QString Qt4MeegoTarget::defaultDisplayName()
{
return QApplication::translate("Qt4ProjectManager::Qt4Target",
diff --git a/src/plugins/madde/qt4maemotarget.h b/src/plugins/madde/qt4maemotarget.h
index 4489521981..3be2877d96 100644
--- a/src/plugins/madde/qt4maemotarget.h
+++ b/src/plugins/madde/qt4maemotarget.h
@@ -57,7 +57,7 @@ class AbstractQt4MaemoTarget : public RemoteLinux::AbstractEmbeddedLinuxTarget
Q_OBJECT
public:
explicit AbstractQt4MaemoTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id,
- const QString &supportedOsType, const QString &qmakeScope);
+ const QString &qmakeScope);
virtual ~AbstractQt4MaemoTarget();
void createApplicationProFiles(bool reparse);
@@ -120,7 +120,7 @@ class AbstractDebBasedQt4MaemoTarget : public AbstractQt4MaemoTarget
Q_OBJECT
public:
AbstractDebBasedQt4MaemoTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id,
- const QString &supportedOsType, const QString &qmakeScope);
+ const QString &qmakeScope);
~AbstractDebBasedQt4MaemoTarget();
QString debianDirPath() const;
@@ -193,7 +193,7 @@ class AbstractRpmBasedQt4MaemoTarget : public AbstractQt4MaemoTarget
Q_OBJECT
public:
AbstractRpmBasedQt4MaemoTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id,
- const QString &supportedOsType, const QString &qmakeScope);
+ const QString &qmakeScope);
~AbstractRpmBasedQt4MaemoTarget();
virtual bool allowsRemoteMounts() const { return false; }
@@ -240,6 +240,8 @@ public:
const QString &id);
virtual ~Qt4Maemo5Target();
+ virtual bool supportsDevice(const QSharedPointer<const ProjectExplorer::IDevice> &device) const;
+
virtual bool allowsRemoteMounts() const { return true; }
virtual bool allowsPackagingDisabling() const { return true; }
virtual bool allowsQmlDebugging() const { return false; }
@@ -264,6 +266,8 @@ public:
const QString &id);
virtual ~Qt4HarmattanTarget();
+ virtual bool supportsDevice(const QSharedPointer<const ProjectExplorer::IDevice> &device) const;
+
virtual bool allowsRemoteMounts() const { return false; }
virtual bool allowsPackagingDisabling() const { return false; }
virtual bool allowsQmlDebugging() const { return true; }
@@ -289,6 +293,7 @@ public:
explicit Qt4MeegoTarget(Qt4ProjectManager::Qt4Project *parent,
const QString &id);
virtual ~Qt4MeegoTarget();
+ virtual bool supportsDevice(const QSharedPointer<const ProjectExplorer::IDevice> &device) const;
static QString defaultDisplayName();
private:
virtual QString specFileName() const;
diff --git a/src/plugins/mercurial/mercurialplugin.h b/src/plugins/mercurial/mercurialplugin.h
index f020d67244..17596be5f3 100644
--- a/src/plugins/mercurial/mercurialplugin.h
+++ b/src/plugins/mercurial/mercurialplugin.h
@@ -68,6 +68,7 @@ class MercurialSettings;
class MercurialPlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Mercurial.json")
public:
MercurialPlugin();
diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h
index 1c2f7ed36c..51ae8cc2bd 100644
--- a/src/plugins/perforce/perforceplugin.h
+++ b/src/plugins/perforce/perforceplugin.h
@@ -80,6 +80,7 @@ struct PerforceResponse
class PerforcePlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Perforce.json")
public:
PerforcePlugin();
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index e1058767cd..0bfef636e1 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -58,7 +58,7 @@ linux-* {
include(../../qtcreator.pri)
-contains(QT_CONFIG, declarative) {
+contains(QT_CONFIG, declarative)|contains(QT_CONFIG, quick1) {
SUBDIRS += \
plugin_qmlprojectmanager \
plugin_qmljsinspector
@@ -120,6 +120,7 @@ plugin_vcsbase.depends = plugin_find
plugin_vcsbase.depends += plugin_texteditor
plugin_vcsbase.depends += plugin_coreplugin
plugin_vcsbase.depends += plugin_projectexplorer
+plugin_vcsbase.depends += plugin_cpptools
plugin_perforce.subdir = perforce
plugin_perforce.depends = plugin_vcsbase
@@ -253,6 +254,7 @@ plugin_qmldesigner.depends += plugin_qmljseditor
plugin_qmldesigner.depends += plugin_projectexplorer
plugin_qmldesigner.depends += plugin_qt4projectmanager
plugin_qmldesigner.depends += plugin_qmlprojectmanager
+plugin_qmldesigner.depends += plugin_cpptools
plugin_qmljsinspector.subdir = qmljsinspector
plugin_qmljsinspector.depends += plugin_projectexplorer
@@ -283,6 +285,7 @@ plugin_analyzerbase.subdir = analyzerbase
plugin_analyzerbase.depends = plugin_coreplugin
plugin_analyzerbase.depends += plugin_projectexplorer
plugin_analyzerbase.depends += plugin_remotelinux
+plugin_analyzerbase.depends += plugin_cpptools
plugin_valgrind.subdir = valgrind
plugin_valgrind.depends = plugin_coreplugin
@@ -300,6 +303,7 @@ plugin_qmljstools.depends = plugin_projectexplorer
plugin_qmljstools.depends += plugin_coreplugin
plugin_qmljstools.depends += plugin_texteditor
plugin_qmljstools.depends += plugin_qtsupport
+plugin_qmljstools.depends += plugin_cpptools
plugin_macros.subdir = macros
plugin_macros.depends = plugin_texteditor
@@ -311,4 +315,4 @@ plugin_todo.subdir = todo
plugin_todo.depends = plugin_coreplugin
plugin_todo.depends += plugin_projectexplorer
plugin_todo.depends += plugin_texteditor
-plugin_todo.depends += plugin_cpptools \ No newline at end of file
+plugin_todo.depends += plugin_cpptools
diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp
index 983d50d0a6..361a146300 100644
--- a/src/plugins/projectexplorer/buildmanager.cpp
+++ b/src/plugins/projectexplorer/buildmanager.cpp
@@ -119,7 +119,7 @@ BuildManagerPrivate::BuildManagerPrivate() :
{
}
-BuildManager::BuildManager(ProjectExplorerPlugin *parent)
+BuildManager::BuildManager(ProjectExplorerPlugin *parent, QAction *cancelBuildAction)
: QObject(parent), d(new BuildManagerPrivate)
{
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
@@ -138,10 +138,10 @@ BuildManager::BuildManager(ProjectExplorerPlugin *parent)
connect(parent->session(), SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
this, SLOT(aboutToRemoveProject(ProjectExplorer::Project*)));
- d->m_outputWindow = new Internal::CompileOutputWindow(this);
+ d->m_outputWindow = new Internal::CompileOutputWindow(this, cancelBuildAction);
pm->addObject(d->m_outputWindow);
- d->m_taskHub = pm->getObject<TaskHub>();
+ d->m_taskHub = ProjectExplorerPlugin::instance()->taskHub();
d->m_taskWindow = new Internal::TaskWindow(d->m_taskHub);
pm->addObject(d->m_taskWindow);
diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h
index eff114e8b8..0ae6c6f84c 100644
--- a/src/plugins/projectexplorer/buildmanager.h
+++ b/src/plugins/projectexplorer/buildmanager.h
@@ -51,7 +51,7 @@ class PROJECTEXPLORER_EXPORT BuildManager : public QObject
Q_OBJECT
public:
- explicit BuildManager(ProjectExplorerPlugin *parent);
+ explicit BuildManager(ProjectExplorerPlugin *parent, QAction *cancelBuildAction);
virtual ~BuildManager();
void extensionsInitialized();
diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp
index 2b941dc87c..28f8bf07da 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.cpp
+++ b/src/plugins/projectexplorer/compileoutputwindow.cpp
@@ -37,6 +37,7 @@
#include "projectexplorerconstants.h"
#include "projectexplorer.h"
#include "projectexplorersettings.h"
+#include "taskhub.h"
#include <coreplugin/icontext.h>
#include <find/basetextfind.h>
@@ -51,6 +52,7 @@
#include <QTextEdit>
#include <QScrollBar>
#include <QPlainTextEdit>
+#include <QToolButton>
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
@@ -59,16 +61,59 @@ namespace {
const int MAX_LINECOUNT = 50000;
}
-CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/)
+namespace ProjectExplorer {
+namespace Internal {
+
+class CompileOutputTextEdit : public Core::OutputWindow
+{
+public:
+ CompileOutputTextEdit(const Core::Context &context) : Core::OutputWindow(context)
+ {
+ }
+
+ void addTask(const Task &task, int blocknumber)
+ {
+ m_taskids.insert(blocknumber, task.taskId);
+ }
+
+ void clearTasks()
+ {
+ m_taskids.clear();
+ }
+
+protected:
+ void mouseDoubleClickEvent(QMouseEvent *ev)
+ {
+ int line = cursorForPosition(ev->pos()).block().blockNumber();
+ if (unsigned taskid = m_taskids.value(line, 0)) {
+ ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
+ TaskHub *hub = pm->getObject<TaskHub>();
+ hub->showTaskInEditor(taskid);
+ } else {
+ QPlainTextEdit::mouseDoubleClickEvent(ev);
+ }
+ }
+
+private:
+ QHash<int, unsigned int> m_taskids; //Map blocknumber to taskId
+};
+
+} // namespace Internal
+} // namespace ProjectExplorer
+
+CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/, QAction *cancelBuildAction) :
+ m_cancelBuildButton(new QToolButton)
{
Core::Context context(Constants::C_COMPILE_OUTPUT);
- m_outputWindow = new Core::OutputWindow(context);
+ m_outputWindow = new CompileOutputTextEdit(context);
m_outputWindow->setWindowTitle(tr("Compile Output"));
m_outputWindow->setWindowIcon(QIcon(QLatin1String(Constants::ICON_WINDOW)));
m_outputWindow->setReadOnly(true);
m_outputWindow->setUndoRedoEnabled(false);
m_outputWindow->setMaxLineCount(MAX_LINECOUNT);
+ m_cancelBuildButton->setDefaultAction(cancelBuildAction);
+
Aggregation::Aggregate *agg = new Aggregation::Aggregate;
agg->add(m_outputWindow);
agg->add(new Find::BaseTextFind(m_outputWindow));
@@ -86,6 +131,7 @@ CompileOutputWindow::~CompileOutputWindow()
{
ExtensionSystem::PluginManager::instance()->removeObject(m_handler);
delete m_handler;
+ delete m_cancelBuildButton;
}
void CompileOutputWindow::updateWordWrapMode()
@@ -113,6 +159,11 @@ QWidget *CompileOutputWindow::outputWidget(QWidget *)
return m_outputWindow;
}
+QList<QWidget *> CompileOutputWindow::toolBarWidgets() const
+{
+ return QList<QWidget *>() << m_cancelBuildButton;
+}
+
static QColor mix_colors(QColor a, QColor b)
{
return QColor((a.red() + 2 * b.red()) / 3, (a.green() + 2 * b.green()) / 3,
@@ -148,6 +199,7 @@ void CompileOutputWindow::appendText(const QString &text, ProjectExplorer::Build
void CompileOutputWindow::clearContents()
{
m_outputWindow->clear();
+ m_outputWindow->clearTasks();
m_taskPositions.clear();
}
@@ -191,7 +243,9 @@ void CompileOutputWindow::registerPositionOf(const Task &task)
int blocknumber = m_outputWindow->blockCount();
if (blocknumber > MAX_LINECOUNT)
return;
+
m_taskPositions.insert(task.taskId, blocknumber);
+ m_outputWindow->addTask(task, blocknumber);
}
bool CompileOutputWindow::knowsPositionOf(const Task &task)
diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h
index 6a1844207d..676bbf007f 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.h
+++ b/src/plugins/projectexplorer/compileoutputwindow.h
@@ -42,6 +42,7 @@
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
class QTextCharFormat;
+class QToolButton;
QT_END_NAMESPACE
namespace ProjectExplorer {
@@ -52,17 +53,18 @@ class Task;
namespace Internal {
class ShowOutputTaskHandler;
+class CompileOutputTextEdit;
class CompileOutputWindow : public Core::IOutputPane
{
Q_OBJECT
public:
- CompileOutputWindow(BuildManager *bm);
+ CompileOutputWindow(BuildManager *bm, QAction *cancelBuildAction);
~CompileOutputWindow();
QWidget *outputWidget(QWidget *);
- QList<QWidget *> toolBarWidgets() const { return QList<QWidget *>(); }
+ QList<QWidget *> toolBarWidgets() const;
QString displayName() const { return tr("Compile Output"); }
int priorityInStatusBar() const;
void clearContents();
@@ -86,9 +88,10 @@ private slots:
void updateWordWrapMode();
private:
- Core::OutputWindow *m_outputWindow;
+ CompileOutputTextEdit *m_outputWindow;
QHash<unsigned int, int> m_taskPositions;
ShowOutputTaskHandler * m_handler;
+ QToolButton *m_cancelBuildButton;
};
} // namespace Internal
diff --git a/src/plugins/projectexplorer/deployconfiguration.cpp b/src/plugins/projectexplorer/deployconfiguration.cpp
index e1d67a7a43..2499ad7f19 100644
--- a/src/plugins/projectexplorer/deployconfiguration.cpp
+++ b/src/plugins/projectexplorer/deployconfiguration.cpp
@@ -94,6 +94,16 @@ DeployConfigurationWidget *DeployConfiguration::configurationWidget() const
return 0;
}
+bool DeployConfiguration::isEnabled() const
+{
+ return false;
+}
+
+QString DeployConfiguration::disabledReason() const
+{
+ return QString();
+}
+
bool DeployConfiguration::fromMap(const QVariantMap &map)
{
if (!ProjectConfiguration::fromMap(map))
diff --git a/src/plugins/projectexplorer/deployconfiguration.h b/src/plugins/projectexplorer/deployconfiguration.h
index cc2844a6e3..cc7bebf3d7 100644
--- a/src/plugins/projectexplorer/deployconfiguration.h
+++ b/src/plugins/projectexplorer/deployconfiguration.h
@@ -63,8 +63,14 @@ public:
virtual DeployConfigurationWidget *configurationWidget() const;
+ virtual bool isEnabled() const;
+ virtual QString disabledReason() const;
+
Target *target() const;
+signals:
+ void enabledChanged();
+
protected:
DeployConfiguration(Target *target, const QString &id);
DeployConfiguration(Target *target, DeployConfiguration *source);
diff --git a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.cpp b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
index 5a3f495960..7ce9e57c7a 100644
--- a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
@@ -29,28 +29,29 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
-#include "linuxdevicefactoryselectiondialog.h"
-#include "ui_linuxdevicefactoryselectiondialog.h"
+#include "devicefactoryselectiondialog.h"
+#include "ui_devicefactoryselectiondialog.h"
-#include "linuxdeviceconfiguration.h"
+#include "idevice.h"
+#include "idevicefactory.h"
#include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h>
#include <QPushButton>
-namespace RemoteLinux {
+namespace ProjectExplorer {
namespace Internal {
-LinuxDeviceFactorySelectionDialog::LinuxDeviceFactorySelectionDialog(QWidget *parent) :
- QDialog(parent), ui(new Ui::LinuxDeviceFactorySelectionDialog)
+DeviceFactorySelectionDialog::DeviceFactorySelectionDialog(QWidget *parent) :
+ QDialog(parent), ui(new Ui::DeviceFactorySelectionDialog)
{
ui->setupUi(this);
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start Wizard"));
- const QList<ILinuxDeviceConfigurationFactory *> &factories
- = ExtensionSystem::PluginManager::instance()->getObjects<ILinuxDeviceConfigurationFactory>();
- foreach (const ILinuxDeviceConfigurationFactory * const factory, factories) {
+ const QList<IDeviceFactory *> &factories
+ = ExtensionSystem::PluginManager::instance()->getObjects<IDeviceFactory>();
+ foreach (const IDeviceFactory * const factory, factories) {
m_factories << factory;
ui->listWidget->addItem(factory->displayName());
}
@@ -59,21 +60,21 @@ LinuxDeviceFactorySelectionDialog::LinuxDeviceFactorySelectionDialog(QWidget *pa
handleItemSelectionChanged();
}
-LinuxDeviceFactorySelectionDialog::~LinuxDeviceFactorySelectionDialog()
+DeviceFactorySelectionDialog::~DeviceFactorySelectionDialog()
{
delete ui;
}
-void LinuxDeviceFactorySelectionDialog::handleItemSelectionChanged()
+void DeviceFactorySelectionDialog::handleItemSelectionChanged()
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!ui->listWidget->selectedItems().isEmpty());
}
-const ILinuxDeviceConfigurationFactory *LinuxDeviceFactorySelectionDialog::selectedFactory() const
+const IDeviceFactory *DeviceFactorySelectionDialog::selectedFactory() const
{
return m_factories.at(ui->listWidget->row(ui->listWidget->selectedItems().first()));
}
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
diff --git a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.h b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.h
index 3d6fd0ce72..f56ce34ba8 100644
--- a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.h
+++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.h
@@ -30,35 +30,35 @@
**
**************************************************************************/
-#ifndef LINUXDEVICEFACTORYSELECTIONDIALOG_H
-#define LINUXDEVICEFACTORYSELECTIONDIALOG_H
+#ifndef DEVICEFACTORYSELECTIONDIALOG_H
+#define DEVICEFACTORYSELECTIONDIALOG_H
#include <QList>
#include <QDialog>
-namespace RemoteLinux {
-class ILinuxDeviceConfigurationFactory;
+namespace ProjectExplorer {
+class IDeviceFactory;
namespace Internal {
-namespace Ui { class LinuxDeviceFactorySelectionDialog; }
+namespace Ui { class DeviceFactorySelectionDialog; }
-class LinuxDeviceFactorySelectionDialog : public QDialog
+class DeviceFactorySelectionDialog : public QDialog
{
Q_OBJECT
public:
- explicit LinuxDeviceFactorySelectionDialog(QWidget *parent = 0);
- ~LinuxDeviceFactorySelectionDialog();
- const ILinuxDeviceConfigurationFactory *selectedFactory() const;
+ explicit DeviceFactorySelectionDialog(QWidget *parent = 0);
+ ~DeviceFactorySelectionDialog();
+ const IDeviceFactory *selectedFactory() const;
private:
Q_SLOT void handleItemSelectionChanged();
- Ui::LinuxDeviceFactorySelectionDialog *ui;
- QList<const ILinuxDeviceConfigurationFactory *> m_factories;
+ Ui::DeviceFactorySelectionDialog *ui;
+ QList<const IDeviceFactory *> m_factories;
};
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
-#endif // LINUXDEVICEFACTORYSELECTIONDIALOG_H
+#endif // DEVICEFACTORYSELECTIONDIALOG_H
diff --git a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.ui b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.ui
index 9cbcddf434..48f26d3021 100644
--- a/src/plugins/remotelinux/linuxdevicefactoryselectiondialog.ui
+++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>RemoteLinux::Internal::LinuxDeviceFactorySelectionDialog</class>
- <widget class="QDialog" name="RemoteLinux::Internal::LinuxDeviceFactorySelectionDialog">
+ <class>ProjectExplorer::Internal::DeviceFactorySelectionDialog</class>
+ <widget class="QDialog" name="ProjectExplorer::Internal::DeviceFactorySelectionDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -54,7 +54,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceFactorySelectionDialog</receiver>
+ <receiver>ProjectExplorer::Internal::DeviceFactorySelectionDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@@ -70,7 +70,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceFactorySelectionDialog</receiver>
+ <receiver>ProjectExplorer::Internal::DeviceFactorySelectionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
new file mode 100644
index 0000000000..4afb2132b0
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -0,0 +1,431 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "devicemanager.h"
+
+#include "idevicefactory.h"
+
+#include <coreplugin/icore.h>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/persistentsettings.h>
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+#include <QHash>
+#include <QList>
+#include <QMainWindow>
+#include <QSettings>
+#include <QString>
+#include <QVariantHash>
+#include <QVariantList>
+
+#include <limits>
+
+namespace ProjectExplorer {
+namespace Internal {
+
+static IDevice::Ptr findAutoDetectedDevice(const QList<IDevice::Ptr> &deviceList,
+ const QString &type, const QString &fingerprint)
+{
+ foreach (const IDevice::Ptr &device, deviceList) {
+ if (device->isAutoDetected() && device->type() == type
+ && device->fingerprint() == fingerprint) {
+ return device;
+ }
+ }
+ return IDevice::Ptr();
+}
+
+const char DeviceManagerKey[] = "DeviceManager";
+const char DeviceListKey[] = "DeviceList";
+const char DefaultDevicesKey[] = "DefaultDevices";
+
+class DeviceManagerPrivate
+{
+public:
+ static DeviceManager *clonedInstance;
+ QList<IDevice::Ptr> devices;
+ QList<IDevice::Ptr> inactiveAutoDetectedDevices;
+ QHash<QString, IDevice::Id> defaultDevices;
+};
+DeviceManager *DeviceManagerPrivate::clonedInstance = 0;
+
+} // namespace Internal
+
+using namespace Internal;
+
+
+DeviceManager *DeviceManager::instance()
+{
+ static DeviceManager deviceManager(true);
+ return &deviceManager;
+}
+
+int DeviceManager::deviceCount() const
+{
+ return d->devices.count();
+}
+
+void DeviceManager::replaceInstance()
+{
+ copy(DeviceManagerPrivate::clonedInstance, instance(), false);
+ instance()->save();
+ emit instance()->deviceListChanged();
+ emit instance()->updated();
+}
+
+void DeviceManager::removeClonedInstance()
+{
+ delete DeviceManagerPrivate::clonedInstance;
+ DeviceManagerPrivate::clonedInstance = 0;
+}
+
+DeviceManager *DeviceManager::cloneInstance()
+{
+ QTC_ASSERT(!DeviceManagerPrivate::clonedInstance, return 0);
+
+ DeviceManagerPrivate::clonedInstance = new DeviceManager;
+ copy(instance(), DeviceManagerPrivate::clonedInstance, true);
+ return DeviceManagerPrivate::clonedInstance;
+}
+
+void DeviceManager::copy(const DeviceManager *source, DeviceManager *target, bool deep)
+{
+ if (deep) {
+ foreach (const IDevice::ConstPtr &device, source->d->devices)
+ target->d->devices << device->clone();
+ } else {
+ target->d->devices = source->d->devices;
+ }
+ target->d->defaultDevices = source->d->defaultDevices;
+}
+
+void DeviceManager::save()
+{
+ Utils::PersistentSettingsWriter writer;
+ writer.saveValue(QLatin1String(DeviceManagerKey), toMap());
+ writer.save(settingsFilePath(), QLatin1String("QtCreatorDevices"), Core::ICore::mainWindow());
+}
+
+void DeviceManager::load()
+{
+ Utils::PersistentSettingsReader reader;
+ if (reader.load(settingsFilePath()))
+ fromMap(reader.restoreValues().value(QLatin1String(DeviceManagerKey)).toMap());
+ else
+ loadPre2_6();
+ ensureOneDefaultDevicePerType();
+}
+
+// TODO: Remove in 2.8
+void DeviceManager::loadPre2_6()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("MaemoDeviceConfigs"));
+ const QVariantHash defaultDevsHash = settings->value(QLatin1String("DefaultConfigs")).toHash();
+ for (QVariantHash::ConstIterator it = defaultDevsHash.constBegin();
+ it != defaultDevsHash.constEnd(); ++it) {
+ d->defaultDevices.insert(it.key(), it.value().toULongLong());
+ }
+ int count = settings->beginReadArray(QLatin1String("ConfigList"));
+ for (int i = 0; i < count; ++i) {
+ settings->setArrayIndex(i);
+ QVariantMap map;
+ foreach (const QString &key, settings->childKeys())
+ map.insert(key, settings->value(key));
+ const IDeviceFactory *factory = factoryForDeviceType(IDevice::typeFromMap(map));
+ if (!factory)
+ continue;
+ IDevice::Ptr device = factory->loadDevice(map);
+ QTC_ASSERT(device, continue);
+ if (device->internalId() == IDevice::invalidId())
+ device->setInternalId(unusedId());
+ d->devices << device;
+ }
+ settings->endArray();
+ settings->endGroup();
+}
+
+void DeviceManager::fromMap(const QVariantMap &map)
+{
+ const QVariantMap defaultDevsMap = map.value(QLatin1String(DefaultDevicesKey)).toMap();
+ for (QVariantMap::ConstIterator it = defaultDevsMap.constBegin();
+ it != defaultDevsMap.constEnd(); ++it) {
+ d->defaultDevices.insert(it.key(), it.value().toULongLong());
+ }
+ const QVariantList deviceList = map.value(QLatin1String(DeviceListKey)).toList();
+ foreach (const QVariant &v, deviceList) {
+ const QVariantMap map = v.toMap();
+ const IDeviceFactory * const factory = factoryForDeviceType(IDevice::typeFromMap(map));
+ if (!factory)
+ continue;
+ IDevice::Ptr device = factory->loadDevice(map);
+ QTC_ASSERT(device, continue);
+ if (device->internalId() == IDevice::invalidId())
+ device->setInternalId(unusedId());
+ if (device->isAutoDetected())
+ d->inactiveAutoDetectedDevices << device;
+ else
+ d->devices << device;
+ }
+}
+
+QVariantMap DeviceManager::toMap() const
+{
+ QVariantMap map;
+ QVariantMap defaultDeviceMap;
+ typedef QHash<QString, IDevice::Id> TypeIdHash;
+ for (TypeIdHash::ConstIterator it = d->defaultDevices.constBegin();
+ it != d->defaultDevices.constEnd(); ++it) {
+ defaultDeviceMap.insert(it.key(), it.value());
+ }
+ map.insert(QLatin1String(DefaultDevicesKey), defaultDeviceMap);
+ QVariantList deviceList;
+ foreach (const IDevice::ConstPtr &device, d->devices)
+ deviceList << device->toMap();
+ foreach (const IDevice::ConstPtr &device, d->inactiveAutoDetectedDevices)
+ deviceList << device->toMap();
+ map.insert(QLatin1String(DeviceListKey), deviceList);
+ return map;
+}
+
+QString DeviceManager::settingsFilePath()
+{
+ ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
+ return QFileInfo(pm->settings()->fileName()).absolutePath() + QLatin1String("/devices.xml");
+}
+
+void DeviceManager::addDevice(const IDevice::Ptr &device)
+{
+ QTC_ASSERT(this != instance() || (device->isAutoDetected()), return);
+ QTC_ASSERT(!device->isAutoDetected() || !findAutoDetectedDevice(d->devices, device->type(),
+ device->fingerprint()), return);
+
+ // Ensure uniqueness of name.
+ QString name = device->displayName();
+ if (hasDevice(name)) {
+ const QString nameTemplate = name + QLatin1String(" (%1)");
+ int suffix = 2;
+ do
+ name = nameTemplate.arg(QString::number(suffix++));
+ while (hasDevice(name));
+ }
+ device->setDisplayName(name);
+ device->setInternalId(unusedId());
+ if (!defaultDevice(device->type()))
+ d->defaultDevices.insert(device->type(), device->internalId());
+ d->devices << device;
+ if (this == instance() && d->clonedInstance)
+ d->clonedInstance->addDevice(device->clone());
+ if (this == instance()) {
+ QList<IDevice::Ptr>::Iterator it = d->inactiveAutoDetectedDevices.begin();
+ while (it != d->inactiveAutoDetectedDevices.end()) {
+ if (it->data()->type() == device->type()
+ && it->data()->fingerprint() == device->fingerprint()) {
+ d->inactiveAutoDetectedDevices.erase(it);
+ break;
+ }
+ ++it;
+ }
+ }
+
+ emit deviceAdded(device);
+ emit updated();
+}
+
+void DeviceManager::removeDevice(int idx)
+{
+ const IDevice::Ptr device = mutableDeviceAt(idx);
+ QTC_ASSERT(device, return);
+ QTC_ASSERT(this != instance() || device->isAutoDetected(), return);
+
+ const bool wasDefault = d->defaultDevices.value(device->type()) == device->internalId();
+ const QString deviceType = device->type();
+ d->devices.removeAt(idx);
+ emit deviceRemoved(idx);
+
+ if (wasDefault) {
+ for (int i = 0; i < d->devices.count(); ++i) {
+ if (deviceAt(i)->type() == deviceType) {
+ d->defaultDevices.insert(deviceAt(i)->type(), deviceAt(i)->internalId());
+ emit defaultStatusChanged(i);
+ break;
+ }
+ }
+ }
+ if (this == instance() && d->clonedInstance) {
+ d->clonedInstance->removeDevice(d->clonedInstance->
+ indexForInternalId(device->internalId()));
+ }
+ if (this == instance() && device->isAutoDetected())
+ d->inactiveAutoDetectedDevices << device;
+
+ emit updated();
+}
+
+void DeviceManager::setDeviceDisplayName(int i, const QString &name)
+{
+ QTC_ASSERT(this != instance(), return);
+ QTC_ASSERT(i >= 0 && i < deviceCount(), return);
+
+ d->devices.at(i)->setDisplayName(name);
+ emit displayNameChanged(i);
+}
+
+void DeviceManager::setDefaultDevice(int idx)
+{
+ QTC_ASSERT(this != instance(), return);
+ QTC_ASSERT(idx >= 0 && idx < deviceCount(), return);
+
+ const IDevice::ConstPtr &device = d->devices.at(idx);
+ const IDevice::ConstPtr &oldDefaultDevice = defaultDevice(device->type());
+ if (device == oldDefaultDevice)
+ return;
+ d->defaultDevices.insert(device->type(), device->internalId());
+ emit defaultStatusChanged(idx);
+ for (int i = 0; i < d->devices.count(); ++i) {
+ if (d->devices.at(i) == oldDefaultDevice) {
+ emit defaultStatusChanged(i);
+ break;
+ }
+ }
+
+ emit updated();
+}
+
+const IDeviceFactory *DeviceManager::factoryForDeviceType(const QString &type)
+{
+ const QList<IDeviceFactory *> &factories
+ = ExtensionSystem::PluginManager::instance()->getObjects<IDeviceFactory>();
+ foreach (const IDeviceFactory * const factory, factories) {
+ if (factory->supportsDeviceType(type))
+ return factory;
+ }
+ return 0;
+}
+
+QString DeviceManager::displayNameForDeviceType(const QString &type)
+{
+ if (const IDeviceFactory * const factory = factoryForDeviceType(type))
+ return factory->displayNameForDeviceType(type);
+ return tr("Unknown OS");
+}
+
+DeviceManager::DeviceManager(bool doLoad) : d(new DeviceManagerPrivate)
+{
+ if (doLoad)
+ load();
+}
+
+DeviceManager::~DeviceManager()
+{
+ delete d;
+}
+
+IDevice::ConstPtr DeviceManager::deviceAt(int idx) const
+{
+ QTC_ASSERT(idx >= 0 && idx < deviceCount(), return IDevice::ConstPtr());
+ return d->devices.at(idx);
+}
+
+IDevice::Ptr DeviceManager::mutableDeviceAt(int idx) const
+{
+ QTC_ASSERT(idx >= 0 && idx < deviceCount(), return IDevice::Ptr());
+ return d->devices.at(idx);
+}
+
+bool DeviceManager::hasDevice(const QString &name) const
+{
+ foreach (const IDevice::Ptr &device, d->devices) {
+ if (device->displayName() == name)
+ return true;
+ }
+ return false;
+}
+
+IDevice::ConstPtr DeviceManager::find(IDevice::Id id) const
+{
+ const int index = indexForInternalId(id);
+ return index == -1 ? IDevice::ConstPtr() : deviceAt(index);
+}
+
+IDevice::ConstPtr DeviceManager::findInactiveAutoDetectedDevice(const QString &type,
+ const QString &fingerprint)
+{
+ return findAutoDetectedDevice(d->inactiveAutoDetectedDevices, type, fingerprint);
+}
+
+IDevice::ConstPtr DeviceManager::defaultDevice(const QString &deviceType) const
+{
+ const IDevice::Id id = d->defaultDevices.value(deviceType, IDevice::invalidId());
+ if (id == IDevice::invalidId())
+ return IDevice::ConstPtr();
+ return find(id);
+}
+
+int DeviceManager::indexForInternalId(IDevice::Id internalId) const
+{
+ for (int i = 0; i < d->devices.count(); ++i) {
+ if (deviceAt(i)->internalId() == internalId)
+ return i;
+ }
+ return -1;
+}
+
+IDevice::Id DeviceManager::internalId(const IDevice::ConstPtr &device) const
+{
+ return device ? device->internalId() : IDevice::invalidId();
+}
+
+int DeviceManager::indexOf(const IDevice::ConstPtr &device) const
+{
+ return indexForInternalId(device->internalId());
+}
+
+void DeviceManager::ensureOneDefaultDevicePerType()
+{
+ foreach (const IDevice::Ptr &device, d->devices) {
+ if (!defaultDevice(device->type()))
+ d->defaultDevices.insert(device->type(), device->internalId());
+ }
+}
+
+IDevice::Id DeviceManager::unusedId() const
+{
+ for (IDevice::Id id = 0; id <= std::numeric_limits<IDevice::Id>::max(); ++id) {
+ if (id != IDevice::invalidId() && !find(id))
+ return id;
+ }
+ QTC_CHECK(false);
+ return IDevice::invalidId();
+}
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h
new file mode 100644
index 0000000000..fd94ae0af9
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef DEVICEMANAGER_H
+#define DEVICEMANAGER_H
+
+#include "idevice.h"
+
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QObject>
+
+namespace ProjectExplorer {
+class IDeviceFactory;
+
+namespace Internal {
+class DeviceManagerPrivate;
+class DeviceSettingsWidget;
+} // namespace Internal
+
+class PROJECTEXPLORER_EXPORT DeviceManager : public QObject
+{
+ Q_OBJECT
+ friend class Internal::DeviceSettingsWidget;
+public:
+ ~DeviceManager();
+
+ static DeviceManager *instance();
+
+ int deviceCount() const;
+ IDevice::ConstPtr deviceAt(int index) const;
+ IDevice::ConstPtr find(IDevice::Id id) const;
+ IDevice::ConstPtr findInactiveAutoDetectedDevice(const QString &type,
+ const QString &fingerprint);
+ IDevice::ConstPtr defaultDevice(const QString &deviceType) const;
+ bool hasDevice(const QString &name) const;
+ IDevice::Id internalId(const IDevice::ConstPtr &device) const;
+
+ int indexOf(const IDevice::ConstPtr &device) const;
+
+ void addDevice(const IDevice::Ptr &device);
+ void removeDevice(int index);
+
+ static const IDeviceFactory *factoryForDeviceType(const QString &type);
+ static QString displayNameForDeviceType(const QString &type);
+
+signals:
+ void deviceAdded(const QSharedPointer<const IDevice> &device);
+ void deviceRemoved(int index);
+ void displayNameChanged(int index);
+ void defaultStatusChanged(int index);
+ void deviceListChanged();
+ void updated(); // Emitted for all of the above.
+
+private:
+ DeviceManager(bool doLoad = false);
+
+ void load();
+ void save();
+ void loadPre2_6();
+ void fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+ void ensureOneDefaultDevicePerType();
+ IDevice::Id unusedId() const;
+ int indexForInternalId(IDevice::Id internalId) const;
+
+ // For SettingsWidget.
+ IDevice::Ptr mutableDeviceAt(int index) const;
+ void setDeviceDisplayName(int i, const QString &name);
+ void setDefaultDevice(int index);
+ static DeviceManager *cloneInstance();
+ static void replaceInstance();
+ static void removeClonedInstance();
+
+ static QString settingsFilePath();
+ static void copy(const DeviceManager *source, DeviceManager *target, bool deep);
+
+ Internal::DeviceManagerPrivate * const d;
+};
+
+} // namespace ProjectExplorer
+
+#endif // DEVICEMANAGER_H
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp
new file mode 100644
index 0000000000..1d87fddec1
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp
@@ -0,0 +1,114 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "devicemanagermodel.h"
+
+#include "devicemanager.h"
+
+#include <QString>
+
+namespace ProjectExplorer {
+namespace Internal {
+class DeviceManagerModelPrivate
+{
+public:
+ const DeviceManager *deviceManager;
+ QList<IDevice::ConstPtr> devices;
+};
+} // namespace Internal
+
+DeviceManagerModel::DeviceManagerModel(const DeviceManager *deviceManager, QObject *parent) :
+ QAbstractListModel(parent), d(new Internal::DeviceManagerModelPrivate)
+{
+ d->deviceManager = deviceManager;
+ handleDeviceListChanged();
+ connect(deviceManager, SIGNAL(deviceAdded(QSharedPointer<const IDevice>)),
+ SLOT(handleDeviceAdded(QSharedPointer<const IDevice>)));
+ connect(deviceManager, SIGNAL(deviceRemoved(int)), SLOT(handleDeviceRemoved(int)));
+ connect(deviceManager, SIGNAL(displayNameChanged(int)), SLOT(handleDataChanged(int)));
+ connect(deviceManager, SIGNAL(defaultStatusChanged(int)), SLOT(handleDataChanged(int)));
+ connect(deviceManager, SIGNAL(deviceListChanged()), SLOT(handleDeviceListChanged()));
+}
+
+DeviceManagerModel::~DeviceManagerModel()
+{
+ delete d;
+}
+
+void DeviceManagerModel::handleDeviceAdded(const IDevice::ConstPtr &device)
+{
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ d->devices << device;
+ endInsertRows();
+}
+
+void DeviceManagerModel::handleDeviceRemoved(int idx)
+{
+ beginRemoveRows(QModelIndex(), idx, idx);
+ d->devices.removeAt(idx);
+ endRemoveRows();
+}
+
+void DeviceManagerModel::handleDataChanged(int idx)
+{
+ const QModelIndex changedIndex = index(idx, 0);
+ emit dataChanged(changedIndex, changedIndex);
+}
+
+void DeviceManagerModel::handleDeviceListChanged()
+{
+ beginResetModel();
+ d->devices.clear();
+ for (int i = 0; i < d->deviceManager->deviceCount(); ++i)
+ d->devices << d->deviceManager->deviceAt(i);
+ endResetModel();
+}
+
+int DeviceManagerModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return d->devices.count();
+}
+
+QVariant DeviceManagerModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= rowCount() || role != Qt::DisplayRole)
+ return QVariant();
+ const IDevice::ConstPtr device = d->devices.at(index.row());
+ QString name = device->displayName();
+ if (d->deviceManager->defaultDevice(device->type()) == device) {
+ name = tr("%1 (default for %2)").arg(name,
+ d->deviceManager->displayNameForDeviceType(device->type()));
+ }
+ return name;
+}
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanagermodel.h b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.h
new file mode 100644
index 0000000000..301e1138c2
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef DEVICEMANAGERMODEL_H
+#define DEVICEMANAGERMODEL_H
+
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QAbstractListModel>
+#include <QSharedPointer>
+
+namespace ProjectExplorer {
+namespace Internal { class DeviceManagerModelPrivate; }
+class IDevice;
+class DeviceManager;
+
+class PROJECTEXPLORER_EXPORT DeviceManagerModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit DeviceManagerModel(const DeviceManager *deviceManager, QObject *parent = 0);
+ ~DeviceManagerModel();
+
+private slots:
+ void handleDeviceAdded(const QSharedPointer<const IDevice> &device);
+ void handleDeviceRemoved(int idx);
+ void handleDataChanged(int idx);
+ void handleDeviceListChanged();
+
+private:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ Internal::DeviceManagerModelPrivate * const d;
+};
+
+} // namespace ProjectExplorer
+
+#endif // DEVICEMANAGERMODEL_H
diff --git a/src/plugins/remotelinux/remotelinuxsettingspages.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp
index 817edef0d9..9c1345e653 100644
--- a/src/plugins/remotelinux/remotelinuxsettingspages.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp
@@ -28,83 +28,84 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
-#include "remotelinuxsettingspages.h"
+#include "devicesettingspage.h"
-#include "linuxdeviceconfigurationssettingswidget.h"
-#include "remotelinux_constants.h"
+#include "devicesettingswidget.h"
+
+#include <projectexplorer/projectexplorerconstants.h>
#include <QCoreApplication>
#include <QString>
#include <QIcon>
-namespace RemoteLinux {
+namespace ProjectExplorer {
namespace Internal {
-LinuxDeviceConfigurationsSettingsPage::LinuxDeviceConfigurationsSettingsPage(QObject *parent)
+DeviceSettingsPage::DeviceSettingsPage(QObject *parent)
: Core::IOptionsPage(parent)
{
}
-LinuxDeviceConfigurationsSettingsPage::~LinuxDeviceConfigurationsSettingsPage()
+DeviceSettingsPage::~DeviceSettingsPage()
{
}
-QString LinuxDeviceConfigurationsSettingsPage::id() const
+QString DeviceSettingsPage::id() const
{
return pageId();
}
-QString LinuxDeviceConfigurationsSettingsPage::displayName() const
+QString DeviceSettingsPage::displayName() const
{
- return tr("Device Configurations");
+ return tr("Devices");
}
-QString LinuxDeviceConfigurationsSettingsPage::category() const
+QString DeviceSettingsPage::category() const
{
return pageCategory();
}
-QString LinuxDeviceConfigurationsSettingsPage::displayCategory() const
+QString DeviceSettingsPage::displayCategory() const
{
- return QCoreApplication::translate("RemoteLinux", "Linux Devices");
+ return QCoreApplication::translate("ProjectExplorer", "Devices");
}
-QIcon LinuxDeviceConfigurationsSettingsPage::categoryIcon() const
+QIcon DeviceSettingsPage::categoryIcon() const
{
return QIcon(QLatin1String(":/projectexplorer/images/MaemoDevice.png"));
}
-bool LinuxDeviceConfigurationsSettingsPage::matches(const QString &searchKeyWord) const
+bool DeviceSettingsPage::matches(const QString &searchKeyWord) const
{
return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive);
}
-QWidget *LinuxDeviceConfigurationsSettingsPage::createPage(QWidget *parent)
+QWidget *DeviceSettingsPage::createPage(QWidget *parent)
{
- m_widget = new LinuxDeviceConfigurationsSettingsWidget(parent);
+ m_widget = new DeviceSettingsWidget(parent);
if (m_keywords.isEmpty())
m_keywords = m_widget->searchKeywords();
return m_widget;
}
-void LinuxDeviceConfigurationsSettingsPage::apply()
+void DeviceSettingsPage::apply()
{
m_widget->saveSettings();
}
-void LinuxDeviceConfigurationsSettingsPage::finish()
+void DeviceSettingsPage::finish()
{
}
-QString LinuxDeviceConfigurationsSettingsPage::pageId()
+QString DeviceSettingsPage::pageId()
{
- return QLatin1String(Constants::RemoteLinuxSettingsPageId);
+ return QLatin1String(Constants::DEVICE_SETTINGS_PAGE_ID);
}
-QString LinuxDeviceConfigurationsSettingsPage::pageCategory()
+QString DeviceSettingsPage::pageCategory()
{
- return QLatin1String(Constants::RemoteLinuxSettingsCategory);
+ return QLatin1String(Constants::DEVICE_SETTINGS_CATEGORY);
}
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
diff --git a/src/plugins/remotelinux/remotelinuxsettingspages.h b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h
index f6e20b6eb6..15e690ebea 100644
--- a/src/plugins/remotelinux/remotelinuxsettingspages.h
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h
@@ -28,21 +28,21 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
-#ifndef REMOTELINUXSETTINGSPAGES_H
-#define REMOTELINUXSETTINGSPAGES_H
+#ifndef DEVICESETTINGSPAGE_H
+#define DEVICESETTINGSPAGE_H
#include <coreplugin/dialogs/ioptionspage.h>
-namespace RemoteLinux {
+namespace ProjectExplorer {
namespace Internal {
-class LinuxDeviceConfigurationsSettingsWidget;
+class DeviceSettingsWidget;
-class LinuxDeviceConfigurationsSettingsPage : public Core::IOptionsPage
+class DeviceSettingsPage : public Core::IOptionsPage
{
Q_OBJECT
public:
- LinuxDeviceConfigurationsSettingsPage(QObject *parent = 0);
- ~LinuxDeviceConfigurationsSettingsPage();
+ DeviceSettingsPage(QObject *parent = 0);
+ ~DeviceSettingsPage();
virtual QString id() const;
virtual QString displayName() const;
@@ -59,10 +59,10 @@ public:
private:
QString m_keywords;
- LinuxDeviceConfigurationsSettingsWidget *m_widget;
+ DeviceSettingsWidget *m_widget;
};
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
-#endif // REMOTELINUXSETTINGSPAGES_H
+#endif // DEVICESETTINGSPAGE_H
diff --git a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
index 4184dcb14a..e0a439300a 100644
--- a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
@@ -28,19 +28,20 @@
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
-#include "linuxdeviceconfigurationssettingswidget.h"
+#include "devicesettingswidget.h"
+#include "ui_devicesettingswidget.h"
-#include "ui_linuxdeviceconfigurationssettingswidget.h"
-
-#include "linuxdeviceconfigurations.h"
-#include "linuxdevicefactoryselectiondialog.h"
-#include "portlist.h"
-#include "remotelinuxutils.h"
-#include "sshkeycreationdialog.h"
+#include "devicefactoryselectiondialog.h"
+#include "devicemanager.h"
+#include "devicemanagermodel.h"
+#include "idevice.h"
+#include "idevicefactory.h"
+#include "idevicewidget.h"
+#include "idevicewizard.h"
#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
-#include <utils/ssh/sshremoteprocessrunner.h>
+#include <utils/portlist.h>
#include <QFileInfo>
#include <QRegExp>
@@ -57,19 +58,15 @@
using namespace Core;
using namespace Utils;
-namespace RemoteLinux {
+namespace ProjectExplorer {
namespace Internal {
-namespace {
-const QLatin1String LastDeviceConfigIndexKey("LastDisplayedMaemoDeviceConfig");
-} // anonymous namespace
-
+const char LastDeviceIndexKey[] = "LastDisplayedMaemoDeviceConfig";
class NameValidator : public QValidator
{
public:
- NameValidator(const LinuxDeviceConfigurations *devConfigs,
- QWidget *parent = 0)
- : QValidator(parent), m_devConfigs(devConfigs)
+ NameValidator(const DeviceManager *deviceManager, QWidget *parent = 0)
+ : QValidator(parent), m_deviceManager(deviceManager)
{
}
@@ -78,7 +75,7 @@ public:
virtual State validate(QString &input, int & /* pos */) const
{
if (input.trimmed().isEmpty()
- || (input != m_oldName && m_devConfigs->hasConfig(input)))
+ || (input != m_oldName && m_deviceManager->hasDevice(input)))
return Intermediate;
return Acceptable;
}
@@ -92,42 +89,39 @@ public:
private:
QString m_oldName;
- const LinuxDeviceConfigurations * const m_devConfigs;
+ const DeviceManager * const m_deviceManager;
};
-LinuxDeviceConfigurationsSettingsWidget::LinuxDeviceConfigurationsSettingsWidget(QWidget *parent)
+DeviceSettingsWidget::DeviceSettingsWidget(QWidget *parent)
: QWidget(parent),
- m_ui(new Ui::LinuxDeviceConfigurationsSettingsWidget),
- m_devConfigs(LinuxDeviceConfigurations::cloneInstance()),
- m_nameValidator(new NameValidator(m_devConfigs.data(), this)),
+ m_ui(new Ui::DeviceSettingsWidget),
+ m_deviceManager(DeviceManager::cloneInstance()),
+ m_nameValidator(new NameValidator(m_deviceManager, this)),
m_saveSettingsRequested(false),
m_additionalActionsMapper(new QSignalMapper(this)),
m_configWidget(0)
{
- LinuxDeviceConfigurations::blockCloning();
initGui();
connect(m_additionalActionsMapper, SIGNAL(mapped(QString)),
SLOT(handleAdditionalActionRequest(QString)));
}
-LinuxDeviceConfigurationsSettingsWidget::~LinuxDeviceConfigurationsSettingsWidget()
+DeviceSettingsWidget::~DeviceSettingsWidget()
{
if (m_saveSettingsRequested) {
- Core::ICore::settings()->setValue(LastDeviceConfigIndexKey,
+ Core::ICore::settings()->setValue(QLatin1String(LastDeviceIndexKey),
currentIndex());
- LinuxDeviceConfigurations::replaceInstance(m_devConfigs.data());
+ DeviceManager::replaceInstance();
}
- LinuxDeviceConfigurations::unblockCloning();
+ DeviceManager::removeClonedInstance();
delete m_ui;
}
-QString LinuxDeviceConfigurationsSettingsWidget::searchKeywords() const
+QString DeviceSettingsWidget::searchKeywords() const
{
QString rc;
QTextStream(&rc) << m_ui->configurationLabel->text()
- << ' ' << m_ui->deviceTypeLabel->text()
- << ' ' << m_ui->deviceTypeValueLabel->text()
<< ' ' << m_ui->deviceNameLabel->text()
<< ' ' << m_ui->nameLineEdit->text();
if (m_configWidget)
@@ -135,114 +129,107 @@ QString LinuxDeviceConfigurationsSettingsWidget::searchKeywords() const
return rc;
}
-void LinuxDeviceConfigurationsSettingsWidget::initGui()
+void DeviceSettingsWidget::initGui()
{
m_ui->setupUi(this);
- m_ui->configurationComboBox->setModel(m_devConfigs.data());
+ DeviceManagerModel * const model = new DeviceManagerModel(m_deviceManager, this);
+ m_ui->configurationComboBox->setModel(model);
m_ui->nameLineEdit->setValidator(m_nameValidator);
int lastIndex = Core::ICore::settings()
- ->value(LastDeviceConfigIndexKey, 0).toInt();
+ ->value(QLatin1String(LastDeviceIndexKey), 0).toInt();
if (lastIndex == -1)
lastIndex = 0;
if (lastIndex < m_ui->configurationComboBox->count())
m_ui->configurationComboBox->setCurrentIndex(lastIndex);
connect(m_ui->configurationComboBox, SIGNAL(currentIndexChanged(int)),
- SLOT(currentConfigChanged(int)));
- currentConfigChanged(currentIndex());
+ SLOT(currentDeviceChanged(int)));
+ currentDeviceChanged(currentIndex());
connect(m_ui->defaultDeviceButton, SIGNAL(clicked()),
SLOT(setDefaultDevice()));
}
-void LinuxDeviceConfigurationsSettingsWidget::addConfig()
+void DeviceSettingsWidget::addDevice()
{
- const QList<ILinuxDeviceConfigurationFactory *> &factories
- = ExtensionSystem::PluginManager::instance()->getObjects<ILinuxDeviceConfigurationFactory>();
+ const QList<IDeviceFactory *> &factories
+ = ExtensionSystem::PluginManager::instance()->getObjects<IDeviceFactory>();
if (factories.isEmpty()) // Can't happen, because this plugin provides the generic one.
return;
- LinuxDeviceFactorySelectionDialog d;
+ DeviceFactorySelectionDialog d;
if (d.exec() != QDialog::Accepted)
return;
- const QScopedPointer<ILinuxDeviceConfigurationWizard> wizard(d.selectedFactory()->createWizard(this));
+ const QScopedPointer<IDeviceWizard> wizard(d.selectedFactory()->createWizard(this));
if (wizard->exec() != QDialog::Accepted)
return;
- m_devConfigs->addConfiguration(wizard->deviceConfiguration());
+ m_deviceManager->addDevice(wizard->device());
m_ui->removeConfigButton->setEnabled(true);
m_ui->configurationComboBox->setCurrentIndex(m_ui->configurationComboBox->count()-1);
}
-void LinuxDeviceConfigurationsSettingsWidget::deleteConfig()
+void DeviceSettingsWidget::removeDevice()
{
- m_devConfigs->removeConfiguration(currentIndex());
- if (m_devConfigs->rowCount() == 0)
- currentConfigChanged(-1);
+ m_deviceManager->removeDevice(currentIndex());
+ if (m_deviceManager->deviceCount() == 0)
+ currentDeviceChanged(-1);
}
-void LinuxDeviceConfigurationsSettingsWidget::displayCurrent()
+void DeviceSettingsWidget::displayCurrent()
{
- const LinuxDeviceConfiguration::ConstPtr &current = currentConfig();
- m_ui->defaultDeviceButton->setEnabled(!current->isDefault());
- m_ui->osTypeValueLabel->setText(RemoteLinuxUtils::osTypeToString(current->osType()));
-
- if (current->deviceType() == LinuxDeviceConfiguration::Hardware)
- m_ui->deviceTypeValueLabel->setText(tr("Physical Device"));
- else
- m_ui->deviceTypeValueLabel->setText(tr("Emulator"));
+ const IDevice::ConstPtr &current = currentDevice();
+ m_ui->defaultDeviceButton->setEnabled(
+ m_deviceManager->defaultDevice(current->type()) != current);
+ m_ui->osTypeValueLabel->setText(DeviceManager::displayNameForDeviceType(current->type()));
+ m_ui->autoDetectionValueLabel->setText(current->isAutoDetected()
+ ? tr("Yes (fingerprint is '%1')").arg(current->fingerprint()) : tr("No"));
m_nameValidator->setDisplayName(current->displayName());
m_ui->removeConfigButton->setEnabled(!current->isAutoDetected());
fillInValues();
}
-void LinuxDeviceConfigurationsSettingsWidget::fillInValues()
+void DeviceSettingsWidget::fillInValues()
{
- const LinuxDeviceConfiguration::ConstPtr &current = currentConfig();
+ const IDevice::ConstPtr &current = currentDevice();
m_ui->nameLineEdit->setText(current->displayName());
}
-void LinuxDeviceConfigurationsSettingsWidget::saveSettings()
+void DeviceSettingsWidget::saveSettings()
{
// We must defer this step because of a stupid bug on MacOS. See QTCREATORBUG-1675.
m_saveSettingsRequested = true;
}
-int LinuxDeviceConfigurationsSettingsWidget::currentIndex() const
+int DeviceSettingsWidget::currentIndex() const
{
return m_ui->configurationComboBox->currentIndex();
}
-LinuxDeviceConfiguration::ConstPtr LinuxDeviceConfigurationsSettingsWidget::currentConfig() const
+QSharedPointer<const IDevice> DeviceSettingsWidget::currentDevice() const
{
Q_ASSERT(currentIndex() != -1);
- return m_devConfigs->deviceAt(currentIndex());
+ return m_deviceManager->deviceAt(currentIndex());
}
-void LinuxDeviceConfigurationsSettingsWidget::configNameEditingFinished()
+void DeviceSettingsWidget::deviceNameEditingFinished()
{
if (m_ui->configurationComboBox->count() == 0)
return;
const QString &newName = m_ui->nameLineEdit->text();
- m_devConfigs->setConfigurationName(currentIndex(), newName);
+ m_deviceManager->setDeviceDisplayName(currentIndex(), newName);
m_nameValidator->setDisplayName(newName);
}
-void LinuxDeviceConfigurationsSettingsWidget::setDefaultDevice()
+void DeviceSettingsWidget::setDefaultDevice()
{
- m_devConfigs->setDefaultDevice(currentIndex());
+ m_deviceManager->setDefaultDevice(currentIndex());
m_ui->defaultDeviceButton->setEnabled(false);
}
-void LinuxDeviceConfigurationsSettingsWidget::showGenerateSshKeyDialog()
-{
- SshKeyCreationDialog dialog(this);
- dialog.exec();
-}
-
-void LinuxDeviceConfigurationsSettingsWidget::currentConfigChanged(int index)
+void DeviceSettingsWidget::currentDeviceChanged(int index)
{
qDeleteAll(m_additionalActionButtons);
delete m_configWidget;
@@ -252,13 +239,11 @@ void LinuxDeviceConfigurationsSettingsWidget::currentConfigChanged(int index)
m_ui->osSpecificGroupBox->setEnabled(false);
if (index == -1) {
m_ui->removeConfigButton->setEnabled(false);
- m_ui->generateKeyButton->setEnabled(false);
clearDetails();
m_ui->defaultDeviceButton->setEnabled(false);
} else {
m_ui->removeConfigButton->setEnabled(true);
- m_ui->generateKeyButton->setEnabled(true);
- const ILinuxDeviceConfigurationFactory * const factory = factoryForCurrentConfig();
+ const IDeviceFactory * const factory = factoryForCurrentDevice();
if (factory) {
const QStringList &actionIds = factory->supportedDeviceActionIds();
foreach (const QString &actionId, actionIds) {
@@ -271,11 +256,9 @@ void LinuxDeviceConfigurationsSettingsWidget::currentConfigChanged(int index)
}
if (!m_ui->osSpecificGroupBox->layout())
new QVBoxLayout(m_ui->osSpecificGroupBox);
- m_configWidget = factory->createWidget(m_devConfigs->mutableDeviceAt(currentIndex()),
+ m_configWidget = factory->createWidget(m_deviceManager->mutableDeviceAt(currentIndex()),
m_ui->osSpecificGroupBox);
if (m_configWidget) {
- connect(m_configWidget, SIGNAL(defaultSshKeyFilePathChanged(QString)),
- m_devConfigs.data(), SLOT(setDefaultSshKeyFilePath(QString)));
m_ui->osSpecificGroupBox->layout()->addWidget(m_configWidget);
m_ui->osSpecificGroupBox->setEnabled(factory->isUserEditable());
}
@@ -286,34 +269,28 @@ void LinuxDeviceConfigurationsSettingsWidget::currentConfigChanged(int index)
}
}
-void LinuxDeviceConfigurationsSettingsWidget::clearDetails()
+void DeviceSettingsWidget::clearDetails()
{
m_ui->nameLineEdit->clear();
m_ui->osTypeValueLabel->clear();
- m_ui->deviceTypeValueLabel->clear();
+ m_ui->autoDetectionValueLabel->clear();
}
-const ILinuxDeviceConfigurationFactory *LinuxDeviceConfigurationsSettingsWidget::factoryForCurrentConfig() const
+const IDeviceFactory *DeviceSettingsWidget::factoryForCurrentDevice() const
{
- Q_ASSERT(currentConfig());
- const QList<ILinuxDeviceConfigurationFactory *> &factories
- = ExtensionSystem::PluginManager::instance()->getObjects<ILinuxDeviceConfigurationFactory>();
- foreach (const ILinuxDeviceConfigurationFactory * const factory, factories) {
- if (factory->supportsOsType(currentConfig()->osType()))
- return factory;
- }
- return 0;
+ Q_ASSERT(currentDevice());
+ return DeviceManager::factoryForDeviceType(currentDevice()->type());
}
-void LinuxDeviceConfigurationsSettingsWidget::handleAdditionalActionRequest(const QString &actionId)
+void DeviceSettingsWidget::handleAdditionalActionRequest(const QString &actionId)
{
- const ILinuxDeviceConfigurationFactory * const factory = factoryForCurrentConfig();
+ const IDeviceFactory * const factory = factoryForCurrentDevice();
Q_ASSERT(factory);
- QDialog * const action = factory->createDeviceAction(actionId, currentConfig(), this);
+ QDialog * const action = factory->createDeviceAction(actionId, currentDevice(), this);
if (action)
action->exec();
delete action;
}
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
diff --git a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
index 855bacef80..3194dc407f 100644
--- a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.h
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
@@ -29,71 +29,66 @@
**
**************************************************************************/
-#ifndef LINUXDEVICECONFIGURATIONSSETTINGSWIDGET_H
-#define LINUXDEVICECONFIGURATIONSSETTINGSWIDGET_H
+#ifndef DEVICESETTINGSWIDGET_H
+#define DEVICESETTINGSWIDGET_H
#include <QList>
-#include <QScopedPointer>
#include <QString>
#include <QPushButton>
#include <QWidget>
QT_BEGIN_NAMESPACE
-class QLineEdit;
class QSignalMapper;
QT_END_NAMESPACE
-namespace RemoteLinux {
-class ILinuxDeviceConfigurationFactory;
-class LinuxDeviceConfiguration;
-class LinuxDeviceConfigurations;
-class ILinuxDeviceConfigurationWidget;
+namespace ProjectExplorer {
+class IDevice;
+class DeviceManager;
+class IDeviceFactory;
+class IDeviceWidget;
namespace Internal {
-namespace Ui { class LinuxDeviceConfigurationsSettingsWidget; }
+namespace Ui { class DeviceSettingsWidget; }
class NameValidator;
-class LinuxDeviceConfigurationsSettingsWidget : public QWidget
+class DeviceSettingsWidget : public QWidget
{
Q_OBJECT
public:
- LinuxDeviceConfigurationsSettingsWidget(QWidget *parent);
- ~LinuxDeviceConfigurationsSettingsWidget();
+ DeviceSettingsWidget(QWidget *parent);
+ ~DeviceSettingsWidget();
void saveSettings();
QString searchKeywords() const;
private slots:
- void currentConfigChanged(int index);
- void addConfig();
- void deleteConfig();
- void configNameEditingFinished();
+ void currentDeviceChanged(int index);
+ void addDevice();
+ void removeDevice();
+ void deviceNameEditingFinished();
void setDefaultDevice();
-
- void showGenerateSshKeyDialog();
-
void handleAdditionalActionRequest(const QString &actionId);
private:
void initGui();
void displayCurrent();
- QSharedPointer<const LinuxDeviceConfiguration> currentConfig() const;
+ QSharedPointer<const IDevice> currentDevice() const;
int currentIndex() const;
void clearDetails();
QString parseTestOutput();
void fillInValues();
- const ILinuxDeviceConfigurationFactory *factoryForCurrentConfig() const;
+ const IDeviceFactory *factoryForCurrentDevice() const;
- Ui::LinuxDeviceConfigurationsSettingsWidget *m_ui;
- const QScopedPointer<LinuxDeviceConfigurations> m_devConfigs;
+ Ui::DeviceSettingsWidget *m_ui;
+ DeviceManager * const m_deviceManager;
NameValidator * const m_nameValidator;
bool m_saveSettingsRequested;
QList<QPushButton *> m_additionalActionButtons;
QSignalMapper * const m_additionalActionsMapper;
- ILinuxDeviceConfigurationWidget *m_configWidget;
+ IDeviceWidget *m_configWidget;
};
} // namespace Internal
-} // namespace RemoteLinux
+} // namespace ProjectExplorer
-#endif // LINUXDEVICECONFIGURATIONSSETTINGSWIDGET_H
+#endif // DEVICESETTINGSWIDGET_H
diff --git a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.ui b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.ui
index bdee36b870..66db396b26 100644
--- a/src/plugins/remotelinux/linuxdeviceconfigurationssettingswidget.ui
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.ui
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget</class>
- <widget class="QWidget" name="RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget">
+ <class>ProjectExplorer::Internal::DeviceSettingsWidget</class>
+ <widget class="QWidget" name="ProjectExplorer::Internal::DeviceSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>726</width>
- <height>473</height>
+ <width>829</width>
+ <height>516</height>
</rect>
</property>
<property name="windowTitle">
@@ -24,7 +24,7 @@
<item row="0" column="0">
<widget class="QLabel" name="configurationLabel">
<property name="text">
- <string>&amp;Configuration:</string>
+ <string>&amp;Device:</string>
</property>
<property name="buddy">
<cstring>configurationComboBox</cstring>
@@ -51,27 +51,11 @@
<property name="title">
<string>General</string>
</property>
- <layout class="QFormLayout" name="formLayout_3">
- <property name="fieldGrowthPolicy">
- <enum>QFormLayout::FieldsStayAtSizeHint</enum>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="deviceNameLabel">
- <property name="text">
- <string>&amp;Name:</string>
- </property>
- <property name="buddy">
- <cstring>nameLineEdit</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="nameLineEdit"/>
- </item>
+ <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="oSTypeLabel">
<property name="text">
- <string>OS type:</string>
+ <string>Type:</string>
</property>
</widget>
</item>
@@ -82,15 +66,41 @@
</property>
</widget>
</item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="nameLineEdit"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="deviceNameLabel">
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
- <widget class="QLabel" name="deviceTypeLabel">
+ <widget class="QLabel" name="autoDetectionKeyLabel">
<property name="text">
- <string>Device type:</string>
+ <string>Auto-detected:</string>
</property>
</widget>
</item>
<item row="2" column="1">
- <widget class="QLabel" name="deviceTypeValueLabel">
+ <widget class="QLabel" name="autoDetectionValueLabel">
<property name="text">
<string/>
</property>
@@ -102,7 +112,7 @@
<item>
<widget class="QGroupBox" name="osSpecificGroupBox">
<property name="title">
- <string>OS Type Specific</string>
+ <string>Type Specific</string>
</property>
</widget>
</item>
@@ -160,22 +170,6 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="generateKeyButton">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="toolTip">
- <string>Click here if you do not have an SSH key yet.</string>
- </property>
- <property name="text">
- <string>&amp;Generate SSH Key...</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -204,12 +198,12 @@
<connection>
<sender>addConfigButton</sender>
<signal>clicked()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget</receiver>
- <slot>addConfig()</slot>
+ <receiver>ProjectExplorer::Internal::DeviceSettingsWidget</receiver>
+ <slot>addDevice()</slot>
<hints>
<hint type="sourcelabel">
- <x>697</x>
- <y>33</y>
+ <x>818</x>
+ <y>35</y>
</hint>
<hint type="destinationlabel">
<x>516</x>
@@ -220,8 +214,8 @@
<connection>
<sender>nameLineEdit</sender>
<signal>editingFinished()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget</receiver>
- <slot>configNameEditingFinished()</slot>
+ <receiver>ProjectExplorer::Internal::DeviceSettingsWidget</receiver>
+ <slot>deviceNameEditingFinished()</slot>
<hints>
<hint type="sourcelabel">
<x>204</x>
@@ -234,30 +228,14 @@
</hints>
</connection>
<connection>
- <sender>generateKeyButton</sender>
- <signal>clicked()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget</receiver>
- <slot>showGenerateSshKeyDialog()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>697</x>
- <y>131</y>
- </hint>
- <hint type="destinationlabel">
- <x>358</x>
- <y>0</y>
- </hint>
- </hints>
- </connection>
- <connection>
<sender>removeConfigButton</sender>
<signal>clicked()</signal>
- <receiver>RemoteLinux::Internal::LinuxDeviceConfigurationsSettingsWidget</receiver>
- <slot>deleteConfig()</slot>
+ <receiver>ProjectExplorer::Internal::DeviceSettingsWidget</receiver>
+ <slot>removeDevice()</slot>
<hints>
<hint type="sourcelabel">
- <x>697</x>
- <y>63</y>
+ <x>818</x>
+ <y>67</y>
</hint>
<hint type="destinationlabel">
<x>351</x>
@@ -267,16 +245,16 @@
</connection>
</connections>
<slots>
- <slot>configNameEditingFinished()</slot>
+ <slot>deviceNameEditingFinished()</slot>
<slot>deviceTypeChanged()</slot>
<slot>hostNameEditingFinished()</slot>
<slot>sshPortEditingFinished()</slot>
<slot>timeoutEditingFinished()</slot>
<slot>userNameEditingFinished()</slot>
<slot>passwordEditingFinished()</slot>
- <slot>addConfig()</slot>
+ <slot>addDevice()</slot>
<slot>selectionChanged()</slot>
- <slot>deleteConfig()</slot>
+ <slot>removeDevice()</slot>
<slot>authenticationTypeChanged()</slot>
<slot>keyFileEditingFinished()</slot>
<slot>testConfig()</slot>
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
new file mode 100644
index 0000000000..14c2c0e2e1
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -0,0 +1,221 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "idevice.h"
+
+#include <utils/qtcassert.h>
+
+#include <QString>
+
+/*!
+ * \class ProjectExplorer::IDevice
+ * \brief This is the base class for all devices.
+ *
+ * The term "device" refers
+ * here to some host to which e.g. files can be deployed or on which an application can run.
+ * In the typical case, this would be some sort of embedded computer connected in some way to
+ * the PC on which QtCreator runs. This class itself does not specify a connection protocol; that
+ * kind of detail is to be added by subclasses.
+ * Devices are managed by a \c DeviceManager.
+ * \sa ProjectExplorer::DeviceManager
+ */
+
+/*!
+ * \fn QString ProjectExplorer::IDevice::type() const
+ * \brief Identifies the type of the device.
+ * Devices with the same type share certain abilities.
+ * This attribute is immutable.
+ * \sa ProjectExplorer::IDeviceFactory
+ */
+
+
+/*!
+ * \fn QString ProjectExplorer::IDevice::displayName() const
+ * \brief A free-text name for the device to be displayed in GUI elements.
+ */
+
+/*!
+ * \fn bool ProjectExplorer::IDevice::isAutoDetected() const
+ * \brief True iff the device has been added via some sort of auto-detection mechanism.
+ * Devices that are not auto-detected can only ever be created interactively from the
+ * settings page.
+ * This attribute is immutable.
+ * \sa DeviceSettingsWidget
+ */
+
+/*!
+ * \fn ProjectExplorer::IDevice::Id ProjectExplorer::IDevice::internalId() const
+ * \brief Identify the device internally.
+ */
+
+/*!
+ * \fn QString ProjectExplorer::IDevice::fingerprint() const
+ * \brief Uniquely identifies an auto-detected device.
+ * The fingerprint can later be used to retrieve changes the user has done to the settings
+ * of an auto-detected device so they are not lost when the device goes away, e.g. because
+ * it has been disconnected.
+ * \sa ProjectExplorer::DeviceManager::findInactiveAutoDetectedDevice()
+ */
+
+/*!
+ * \fn ProjectExplorer::IDevice::Id ProjectExplorer::IDevice::invalidId()
+ * \brief A value that no device can ever have as its internal id.
+ */
+
+/*!
+ * \fn ProjectExplorer::IDevice::Ptr ProjectExploer::IDevice::clone() const
+ * \brief Creates an identical copy of a device object.
+ */
+
+/*!
+ * \fn void fromMap(const QVariantMap &map)
+ * \brief Restores a device object from a serialized state as written by \c toMap().
+ * If subclasses override this to restore additional state, they must call the base class
+ * implementation.
+ */
+
+/*!
+ * \fn QVariantMap toMap() const
+ * \brief Serializes a device object, e.g. to save it to a file.
+ * If subclasses override this to save additional state, they must call the base class
+ * implementation.
+ */
+
+namespace ProjectExplorer {
+
+const char DisplayNameKey[] = "Name";
+const char TypeKey[] = "OsType";
+const char InternalIdKey[] = "InternalId";
+const char OriginKey[] = "Origin";
+const char FingerprintKey[] = "FingerPrint";
+
+namespace Internal {
+class IDevicePrivate
+{
+public:
+ QString displayName;
+ QString type;
+ IDevice::Origin origin;
+ IDevice::Id internalId;
+ QString fingerprint;
+};
+} // namespace Internal
+
+IDevice::IDevice() : d(new Internal::IDevicePrivate)
+{
+}
+
+IDevice::IDevice(const QString &type, Origin origin, const QString fingerprint)
+ : d(new Internal::IDevicePrivate)
+{
+ d->type = type;
+ d->origin = origin;
+ d->fingerprint = fingerprint;
+ QTC_CHECK(d->origin == ManuallyAdded || !d->fingerprint.isEmpty());
+}
+
+IDevice::IDevice(const IDevice &other) : d(new Internal::IDevicePrivate)
+{
+ *d = *other.d;
+}
+
+IDevice::~IDevice()
+{
+ delete d;
+}
+
+QString IDevice::displayName() const
+{
+ return d->displayName;
+}
+
+void IDevice::setDisplayName(const QString &name)
+{
+ d->displayName = name;
+}
+
+QString IDevice::type() const
+{
+ return d->type;
+}
+
+bool IDevice::isAutoDetected() const
+{
+ return d->origin == AutoDetected;
+}
+
+QString IDevice::fingerprint() const
+{
+ return d->fingerprint;
+}
+
+IDevice::Id IDevice::internalId() const
+{
+ return d->internalId;
+}
+
+void IDevice::setInternalId(IDevice::Id id)
+{
+ d->internalId = id;
+}
+
+IDevice::Id IDevice::invalidId()
+{
+ return 0;
+}
+
+QString IDevice::typeFromMap(const QVariantMap &map)
+{
+ return map.value(QLatin1String(TypeKey)).toString();
+}
+
+void IDevice::fromMap(const QVariantMap &map)
+{
+ d->type = typeFromMap(map);
+ d->displayName = map.value(QLatin1String(DisplayNameKey)).toString();
+ d->internalId = map.value(QLatin1String(InternalIdKey), invalidId()).toULongLong();
+ d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt());
+ d->fingerprint = map.value(QLatin1String(FingerprintKey)).toString();
+ QTC_CHECK(d->origin == ManuallyAdded || !d->fingerprint.isEmpty());
+}
+
+QVariantMap IDevice::toMap() const
+{
+ QVariantMap map;
+ map.insert(QLatin1String(DisplayNameKey), d->displayName);
+ map.insert(QLatin1String(TypeKey), d->type);
+ map.insert(QLatin1String(InternalIdKey), d->internalId);
+ map.insert(QLatin1String(OriginKey), d->origin);
+ map.insert(QLatin1String(FingerprintKey), d->fingerprint);
+ return map;
+}
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
new file mode 100644
index 0000000000..047d7698d6
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -0,0 +1,89 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef IDEVICE_H
+#define IDEVICE_H
+
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QSharedPointer>
+#include <QVariantMap>
+
+namespace ProjectExplorer {
+namespace Internal { class IDevicePrivate; }
+
+// See cpp file for documentation.
+class PROJECTEXPLORER_EXPORT IDevice
+{
+ friend class DeviceManager;
+public:
+ typedef QSharedPointer<IDevice> Ptr;
+ typedef QSharedPointer<const IDevice> ConstPtr;
+
+ typedef quint64 Id;
+
+ enum Origin { ManuallyAdded, AutoDetected };
+
+ virtual ~IDevice();
+
+ QString displayName() const;
+ void setDisplayName(const QString &name);
+
+ QString type() const;
+ bool isAutoDetected() const;
+ QString fingerprint() const;
+ Id internalId() const;
+
+ virtual void fromMap(const QVariantMap &map);
+ virtual Ptr clone() const = 0;
+
+ static Id invalidId();
+
+ static QString typeFromMap(const QVariantMap &map);
+
+protected:
+ IDevice();
+ IDevice(const QString &type, Origin origin, const QString fingerprint = QString());
+ IDevice(const IDevice &other);
+
+ virtual QVariantMap toMap() const;
+
+private:
+ void setInternalId(Id id);
+
+ IDevice &operator=(const IDevice &);
+
+ Internal::IDevicePrivate *d;
+};
+
+} // namespace ProjectExplorer
+
+#endif // IDEVICE_H
diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.h b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
new file mode 100644
index 0000000000..b55570c98a
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
@@ -0,0 +1,129 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef IDEVICEFACTORY_H
+#define IDEVICEFACTORY_H
+
+#include "idevice.h"
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QObject>
+#include <QStringList>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+class QDialog;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+class IDeviceWidget;
+class IDeviceWizard;
+
+/*!
+ \class ProjectExplorer::IDeviceFactory
+
+ \brief Provides an interface for classes providing services related to certain type of device.
+
+ The factory objects have to be added to the global object pool via
+ \c ExtensionSystem::PluginManager::addObject().
+ \sa ExtensionSystem::PluginManager::addObject()
+*/
+class PROJECTEXPLORER_EXPORT IDeviceFactory : public QObject
+{
+ Q_OBJECT
+
+public:
+ /*!
+ A short, one-line description of what kind of device this factory supports.
+ */
+ virtual QString displayName() const = 0;
+
+ /*!
+ A wizard that can create the types of device this factory supports.
+ */
+ virtual IDeviceWizard *createWizard(QWidget *parent = 0) const = 0;
+
+ /*!
+ Loads a device from a serialized state. The device must be of a matching type.
+ */
+ virtual IDevice::Ptr loadDevice(const QVariantMap &map) const = 0;
+
+ /*!
+ A widget that can configure the device this factory supports.
+ */
+ virtual IDeviceWidget *createWidget(const IDevice::Ptr &device, QWidget *parent = 0) const = 0;
+
+ /*!
+ Returns true iff this factory supports the given device type.
+ */
+ virtual bool supportsDeviceType(const QString &type) const = 0;
+
+ /*!
+ Returns a human-readable string for the given device type, if this factory supports that type.
+ */
+ virtual QString displayNameForDeviceType(const QString &type) const = 0;
+
+ /*!
+ Returns a list of ids representing actions that can be run on devices
+ that this factory supports. These actions will be available in the "Devices"
+ options page.
+ */
+ virtual QStringList supportedDeviceActionIds() const = 0;
+
+ /*!
+ A human-readable string for the given id. Will be displayed on a button which, when clicked,
+ starts the respective action.
+ */
+ virtual QString displayNameForActionId(const QString &actionId) const = 0;
+
+ /*!
+ True iff the user should be allowed to edit the devices created by this
+ factory. Returns true by default. Override if your factory creates fixed configurations
+ for which later editing makes no sense.
+ */
+ virtual bool isUserEditable() const { return true; }
+
+ /*!
+ Produces a dialog implementing the respective action. The dialog is supposed to be
+ modal, so implementers must make sure to make it interruptible as to not needlessly
+ block the UI.
+ */
+ virtual QDialog *createDeviceAction(const QString &actionId, const IDevice::ConstPtr &device,
+ QWidget *parent = 0) const = 0;
+
+protected:
+ IDeviceFactory(QObject *parent) : QObject(parent) { }
+};
+
+} // namespace ProjectExplorer
+
+#endif // IDEVICEFACTORY_H
diff --git a/src/plugins/projectexplorer/devicesupport/idevicewidget.h b/src/plugins/projectexplorer/devicesupport/idevicewidget.h
new file mode 100644
index 0000000000..487592a638
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/idevicewidget.h
@@ -0,0 +1,68 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef IDEVICEWIDGET_H
+#define IDEVICEWIDGET_H
+
+#include "idevice.h"
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QWidget>
+
+namespace ProjectExplorer {
+
+/*!
+ \class ProjectExplorer::IDeviceWidget
+
+ \brief Provides an interface for the widget configuring an IDevice.
+
+ A class implementing this interface will display a widget in the configuration
+ options page "Devices".
+ It is used to let the user configure a particular device.
+*/
+
+class PROJECTEXPLORER_EXPORT IDeviceWidget : public QWidget
+{
+ Q_OBJECT
+
+protected:
+ IDeviceWidget(const IDevice::Ptr &device, QWidget *parent = 0)
+ : QWidget(parent), m_device(device) {}
+
+ IDevice::Ptr device() const { return m_device; }
+
+private:
+ IDevice::Ptr m_device;
+};
+
+} // namespace ProjectExplorer
+
+#endif // IDEVICEWIDGET_H
diff --git a/src/plugins/projectexplorer/devicesupport/idevicewizard.h b/src/plugins/projectexplorer/devicesupport/idevicewizard.h
new file mode 100644
index 0000000000..5e109f0b47
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/idevicewizard.h
@@ -0,0 +1,64 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef IDEVICEWIZARD_H
+#define IDEVICEWIZARD_H
+
+#include "idevice.h"
+#include <projectexplorer/projectexplorer_export.h>
+
+#include <QWizard>
+
+namespace ProjectExplorer {
+
+/*!
+ \class ProjectExplorer::IDeviceWizard
+
+ \brief Provides an interface for wizards creating an IDevice subclass.
+
+ A class implementing this interface is a wizard whose final result is
+ an \c IDevice object. The wizard will be started when the user chooses the
+ "Add..." action from the "Devices" options page.
+*/
+class PROJECTEXPLORER_EXPORT IDeviceWizard : public QWizard
+{
+ Q_OBJECT
+
+public:
+ virtual IDevice::Ptr device() = 0;
+
+protected:
+ IDeviceWizard(QWidget *parent) : QWizard(parent) { }
+};
+
+} // namespace ProjectExplorer
+
+#endif // IDEVICEWIZARD_H
diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp
index ec4c0c91a4..a6b1d6942e 100644
--- a/src/plugins/projectexplorer/gcctoolchain.cpp
+++ b/src/plugins/projectexplorer/gcctoolchain.cpp
@@ -146,10 +146,12 @@ static QByteArray gccPredefinedMacros(const Utils::FileName &gcc, const QStringL
return predefinedMacros;
}
-static QList<HeaderPath> gccHeaderPathes(const Utils::FileName &gcc, const QStringList &env)
+QList<HeaderPath> GccToolChain::gccHeaderPaths(const Utils::FileName &gcc, const QStringList &env, const QString &sysrootPath)
{
QList<HeaderPath> systemHeaderPaths;
QStringList arguments;
+ if (!sysrootPath.isEmpty())
+ arguments.append(QString::fromLatin1("--sysroot=%1").arg(sysrootPath));
arguments << QLatin1String("-xc++")
<< QLatin1String("-E")
<< QLatin1String("-v")
@@ -326,7 +328,7 @@ GccToolChain::GccToolChain(const GccToolChain &tc) :
m_debuggerCommand(tc.debuggerCommand()),
m_targetAbi(tc.m_targetAbi),
m_supportedAbis(tc.m_supportedAbis),
- m_headerPathes(tc.m_headerPathes),
+ m_headerPaths(tc.m_headerPaths),
m_version(tc.m_version)
{ }
@@ -409,13 +411,13 @@ ProjectExplorer::ToolChain::CompilerFlags GccToolChain::compilerFlags(const QStr
QList<HeaderPath> GccToolChain::systemHeaderPaths() const
{
- if (m_headerPathes.isEmpty()) {
+ if (m_headerPaths.isEmpty()) {
// Using a clean environment breaks ccache/distcc/etc.
Utils::Environment env = Utils::Environment::systemEnvironment();
addToEnvironment(env);
- m_headerPathes = gccHeaderPathes(m_compilerCommand, env.toStringList());
+ m_headerPaths = gccHeaderPaths(m_compilerCommand, env.toStringList());
}
- return m_headerPathes;
+ return m_headerPaths;
}
void GccToolChain::addToEnvironment(Utils::Environment &env) const
diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h
index ca9a2c239f..4a77843c16 100644
--- a/src/plugins/projectexplorer/gcctoolchain.h
+++ b/src/plugins/projectexplorer/gcctoolchain.h
@@ -97,6 +97,8 @@ protected:
virtual QList<Abi> detectSupportedAbis() const;
virtual QString detectVersion() const;
+ static QList<HeaderPath> gccHeaderPaths(const Utils::FileName &gcc, const QStringList &env, const QString &sysrootPath = QString());
+
mutable QByteArray m_predefinedMacros;
private:
@@ -109,7 +111,7 @@ private:
Abi m_targetAbi;
mutable QList<Abi> m_supportedAbis;
- mutable QList<HeaderPath> m_headerPathes;
+ mutable QList<HeaderPath> m_headerPaths;
mutable QString m_version;
friend class Internal::GccToolChainFactory;
diff --git a/src/plugins/projectexplorer/iprojectproperties.h b/src/plugins/projectexplorer/iprojectproperties.h
index 110b4f9a53..b86d3351e9 100644
--- a/src/plugins/projectexplorer/iprojectproperties.h
+++ b/src/plugins/projectexplorer/iprojectproperties.h
@@ -87,8 +87,6 @@ class PROJECTEXPLORER_EXPORT IProjectPanelFactory : public IPanelFactory
public:
virtual bool supports(Project *project) = 0;
virtual PropertiesPanel *createPanel(Project *project) = 0;
-signals:
- void projectUpdated(ProjectExplorer::Project *project);
};
class PROJECTEXPLORER_EXPORT ITargetPanelFactory : public IPanelFactory
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index cb60322621..46166a08aa 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -388,4 +388,9 @@ bool Project::needsConfiguration() const
return false;
}
+void Project::configureAsExampleProject(const QStringList &platforms)
+{
+ Q_UNUSED(platforms);
+}
+
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index b4f217c900..712de97233 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -74,8 +74,6 @@ public:
virtual Core::IDocument *document() const = 0;
virtual IProjectManager *projectManager() const = 0;
- virtual QList<Project *> dependsOn() = 0; //NBS TODO implement dependsOn
-
bool hasActiveBuildSettings() const;
// EditorConfiguration:
@@ -118,6 +116,7 @@ public:
void setNamedSettings(const QString &name, QVariant &value);
virtual bool needsConfiguration() const;
+ virtual void configureAsExampleProject(const QStringList &platforms);
signals:
void fileListChanged();
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index 016644bdfd..1611a7356d 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -79,6 +79,7 @@
#include "buildconfiguration.h"
#include "miniprojecttargetselector.h"
#include "taskhub.h"
+#include "devicesupport/devicesettingspage.h"
#include "publishing/ipublishingwizardfactory.h"
#include "publishing/publishingwizardselectiondialog.h"
@@ -375,13 +376,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
addAutoReleasedObject(new CopyTaskHandler);
addAutoReleasedObject(new ShowInEditorTaskHandler);
addAutoReleasedObject(new VcsAnnotateTaskHandler);
-
- d->m_buildManager = new BuildManager(this);
- connect(d->m_buildManager, SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
- this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
- connect(d->m_buildManager, SIGNAL(buildQueueFinished(bool)),
- this, SLOT(buildQueueFinished(bool)));
-
addAutoReleasedObject(new CoreListener);
d->m_outputPane = new AppOutputPane;
@@ -420,8 +414,9 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
addAutoReleasedObject(new ProjectFileWizardExtension);
- // Settings page
+ // Settings pages
addAutoReleasedObject(new ProjectExplorerSettingsPage);
+ addAutoReleasedObject(new DeviceSettingsPage);
// context menus
Core::ActionContainer *msessionContextMenu =
@@ -796,7 +791,9 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
mbuild->addAction(cmd, Constants::G_BUILD_RUN);
// cancel build action
- d->m_cancelBuildAction = new QAction(tr("Cancel Build"), this);
+ QIcon stopIcon = QIcon(QLatin1String(Constants::ICON_STOP));
+ stopIcon.addFile(QLatin1String(Constants::ICON_STOP_SMALL));
+ d->m_cancelBuildAction = new QAction(stopIcon, tr("Cancel Build"), this);
cmd = am->registerAction(d->m_cancelBuildAction, Constants::CANCELBUILD, globalcontext);
mbuild->addAction(cmd, Constants::G_BUILD_CANCEL);
@@ -1001,6 +998,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(this, SIGNAL(updateRunActions()), this, SLOT(slotUpdateRunActions()));
connect(this, SIGNAL(settingsChanged()), this, SLOT(updateRunWithoutDeployMenu()));
+ d->m_buildManager = new BuildManager(this, d->m_cancelBuildAction);
+ connect(d->m_buildManager, SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
+ this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
+ connect(d->m_buildManager, SIGNAL(buildQueueFinished(bool)),
+ this, SLOT(buildQueueFinished(bool)));
+
updateActions();
connect(Core::ICore::instance(), SIGNAL(coreAboutToOpen()),
@@ -1277,7 +1280,7 @@ void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName)
QMessageBox::critical(Core::ICore::mainWindow(), tr("Failed to open project"), errorMessage);
}
-bool ProjectExplorerPlugin::openProject(const QString &fileName, QString *errorString)
+Project *ProjectExplorerPlugin::openProject(const QString &fileName, QString *errorString)
{
if (debug)
qDebug() << "ProjectExplorerPlugin::openProject";
@@ -1286,9 +1289,9 @@ bool ProjectExplorerPlugin::openProject(const QString &fileName, QString *errorS
if (!list.isEmpty()) {
addToRecentProjects(fileName, list.first()->displayName());
d->m_session->setStartupProject(list.first());
- return true;
+ return list.first();
}
- return false;
+ return 0;
}
static inline QList<IProjectManager*> allProjectManagers()
@@ -1865,6 +1868,11 @@ void ProjectExplorerPlugin::buildProject(ProjectExplorer::Project *p)
QStringList(QLatin1String(Constants::BUILDSTEPS_BUILD)));
}
+void ProjectExplorerPlugin::requestProjectModeUpdate(Project *p)
+{
+ d->m_proWindow->projectUpdated(p);
+}
+
void ProjectExplorerPlugin::buildProject()
{
queue(d->m_session->projectOrder(session()->startupProject()),
@@ -2200,14 +2208,14 @@ void ProjectExplorerPlugin::activeRunConfigurationChanged()
if (rc == previousRunConfiguration)
return;
if (previousRunConfiguration) {
- disconnect(previousRunConfiguration, SIGNAL(isEnabledChanged(bool)),
+ disconnect(previousRunConfiguration, SIGNAL(enabledChanged()),
this, SIGNAL(updateRunActions()));
disconnect(previousRunConfiguration->debuggerAspect(), SIGNAL(debuggersChanged()),
this, SIGNAL(updateRunActions()));
}
previousRunConfiguration = rc;
if (rc) {
- connect(rc, SIGNAL(isEnabledChanged(bool)),
+ connect(rc, SIGNAL(enabledChanged()),
this, SIGNAL(updateRunActions()));
connect(rc->debuggerAspect(), SIGNAL(debuggersChanged()),
this, SIGNAL(updateRunActions()));
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index 49145de8f2..8e61dbc71f 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -72,6 +72,7 @@ class PROJECTEXPLORER_EXPORT ProjectExplorerPlugin
: public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ProjectExplorer.json")
public:
ProjectExplorerPlugin();
@@ -79,7 +80,7 @@ public:
static ProjectExplorerPlugin *instance();
- bool openProject(const QString &fileName, QString *error);
+ Project *openProject(const QString &fileName, QString *error);
QList<Project *> openProjects(const QStringList &fileNames, QString *error);
Q_SLOT void openProjectWelcomePage(const QString &fileName);
@@ -126,6 +127,9 @@ public:
void addExistingFiles(const QStringList &filePaths);
void buildProject(ProjectExplorer::Project *p);
+ /// Normally there's no need to call this function.
+ /// This function needs to be called, only if the pages that support a project changed.
+ void requestProjectModeUpdate(ProjectExplorer::Project *p);
QList<RunControl *> runControls() const;
diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro
index 5c7dfe9d19..1727a93388 100644
--- a/src/plugins/projectexplorer/projectexplorer.pro
+++ b/src/plugins/projectexplorer/projectexplorer.pro
@@ -1,8 +1,12 @@
TEMPLATE = lib
TARGET = ProjectExplorer
-QT += xml \
- script \
- declarative
+QT += xml script
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
+
include(../../qtcreatorplugin.pri)
include(projectexplorer_dependencies.pri)
include(../../libs/utils/utils.pri)
@@ -102,7 +106,16 @@ HEADERS += projectexplorer.h \
appoutputpane.h \
codestylesettingspropertiespage.h \
settingsaccessor.h \
- environmentitemswidget.h
+ environmentitemswidget.h \
+ devicesupport/idevice.h \
+ devicesupport/idevicewizard.h \
+ devicesupport/idevicewidget.h \
+ devicesupport/idevicefactory.h \
+ devicesupport/devicemanager.h \
+ devicesupport/devicemanagermodel.h \
+ devicesupport/devicefactoryselectiondialog.h \
+ devicesupport/devicesettingswidget.h \
+ devicesupport/devicesettingspage.h
SOURCES += projectexplorer.cpp \
abi.cpp \
@@ -188,7 +201,13 @@ SOURCES += projectexplorer.cpp \
appoutputpane.cpp \
codestylesettingspropertiespage.cpp \
settingsaccessor.cpp \
- environmentitemswidget.cpp
+ environmentitemswidget.cpp \
+ devicesupport/idevice.cpp \
+ devicesupport/devicemanager.cpp \
+ devicesupport/devicemanagermodel.cpp \
+ devicesupport/devicefactoryselectiondialog.cpp \
+ devicesupport/devicesettingswidget.cpp \
+ devicesupport/devicesettingspage.cpp
FORMS += processstep.ui \
toolchainoptionspage.ui \
@@ -200,7 +219,9 @@ FORMS += processstep.ui \
targetsettingswidget.ui \
doubletabwidget.ui \
publishing/publishingwizardselectiondialog.ui \
- codestylesettingspropertiespage.ui
+ codestylesettingspropertiespage.ui \
+ devicesupport/devicefactoryselectiondialog.ui \
+ devicesupport/devicesettingswidget.ui
WINSOURCES += \
windebuginterface.cpp \
diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs
index 539ec48d03..f559c571aa 100644
--- a/src/plugins/projectexplorer/projectexplorer.qbs
+++ b/src/plugins/projectexplorer/projectexplorer.qbs
@@ -271,7 +271,24 @@ QtcPlugin {
"publishing/publishingwizardselectiondialog.ui",
"publishing/ipublishingwizardfactory.h",
"publishing/publishingwizardselectiondialog.cpp",
- "publishing/publishingwizardselectiondialog.h"
+ "publishing/publishingwizardselectiondialog.h",
+ "devicesupport/idevice.h",
+ "devicesupport/idevice.cpp",
+ "devicesupport/devicemanager.h",
+ "devicesupport/devicemanager.cpp",
+ "devicesupport/devicemanagermodel.h",
+ "devicesupport/devicemanagermodel.cpp",
+ "devicesupport/devicesettingspage.h",
+ "devicesupport/devicesettingspage.cpp",
+ "devicesupport/devicefactoryselectiondialog.cpp",
+ "devicesupport/devicefactoryselectiondialog.h",
+ "devicesupport/devicefactoryselectiondialog.ui",
+ "devicesupport/devicesettingswidget.cpp",
+ "devicesupport/devicesettingswidget.h",
+ "devicesupport/devicesettingswidget.ui",
+ "devicesupport/idevicewizard.h",
+ "devicesupport/idevicewidget.h",
+ "devicesupport/idevicefactory.h"
]
Group {
diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h
index 5c6c18cdf0..decd9d7350 100644
--- a/src/plugins/projectexplorer/projectexplorerconstants.h
+++ b/src/plugins/projectexplorer/projectexplorerconstants.h
@@ -185,6 +185,10 @@ const char PROJECTEXPLORER_SETTINGS_CATEGORY_ICON[] = ":/core/images/category_b
const char PROJECTEXPLORER_SETTINGS_ID[] = "A.ProjectExplorer.ProjectExplorer";
const char TOOLCHAIN_SETTINGS_PAGE_ID[] = "M.ProjectExplorer.ToolChainOptions";
+// Device settings page
+const char DEVICE_SETTINGS_CATEGORY[] = "X.Devices";
+const char DEVICE_SETTINGS_PAGE_ID[] = "AA.Device Settings";
+
// Task categories
const char TASK_CATEGORY_COMPILE[] = "Task.Category.Compile";
const char TASK_CATEGORY_BUILDSYSTEM[] = "Task.Category.Buildsystem";
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index ee4c436d5a..f26e4d2e45 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -32,6 +32,8 @@
#include "projectwelcomepage.h"
+#include "projectexplorerconstants.h"
+
#include <utils/stringutils.h>
#include <QDeclarativeEngine>
@@ -216,6 +218,11 @@ void ProjectWelcomePage::facilitateQml(QDeclarativeEngine *engine)
ctx->setContextProperty(QLatin1String("projectWelcomePage"), this);
}
+ProjectWelcomePage::Id ProjectWelcomePage::id() const
+{
+ return Develop;
+}
+
void ProjectWelcomePage::reloadWelcomeScreenData()
{
if (m_sessionModel)
diff --git a/src/plugins/projectexplorer/projectwelcomepage.h b/src/plugins/projectexplorer/projectwelcomepage.h
index 2e85551b2e..5c0d963a3b 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.h
+++ b/src/plugins/projectexplorer/projectwelcomepage.h
@@ -101,6 +101,7 @@ public:
QWidget *page() { return 0; }
QString title() const { return tr("Develop"); }
int priority() const { return 20; }
+ Id id() const;
void reloadWelcomeScreenData();
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index 5ea53f46c1..ceddef50bd 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -268,10 +268,6 @@ void ProjectWindow::extensionsInitialized()
QList<IProjectPanelFactory *> list = ExtensionSystem::PluginManager::instance()->getObjects<IProjectPanelFactory>();
qSort(list.begin(), list.end(), &IPanelFactory::prioritySort);
- foreach (IProjectPanelFactory *fac, list)
- connect (fac, SIGNAL(projectUpdated(ProjectExplorer::Project*)),
- this, SLOT(projectUpdated(ProjectExplorer::Project*)));
-
}
void ProjectWindow::aboutToShutdown()
diff --git a/src/plugins/projectexplorer/projectwindow.h b/src/plugins/projectexplorer/projectwindow.h
index c4f372a09e..8a5be9f2f9 100644
--- a/src/plugins/projectexplorer/projectwindow.h
+++ b/src/plugins/projectexplorer/projectwindow.h
@@ -82,6 +82,7 @@ public:
void aboutToShutdown();
void extensionsInitialized();
+ void projectUpdated(ProjectExplorer::Project *p);
private slots:
void targetFactoriesChanged();
void showProperties(int index, int subIndex);
@@ -90,7 +91,6 @@ private slots:
void registerProject(ProjectExplorer::Project*);
void deregisterProject(ProjectExplorer::Project*);
void startupProjectChanged(ProjectExplorer::Project *);
- void projectUpdated(ProjectExplorer::Project *p);
private:
bool useTargetPage(ProjectExplorer::Project *project);
diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp
index 846d732b81..925b141f94 100644
--- a/src/plugins/projectexplorer/projectwizardpage.cpp
+++ b/src/plugins/projectexplorer/projectwizardpage.cpp
@@ -34,6 +34,7 @@
#include "ui_projectwizardpage.h"
#include <coreplugin/icore.h>
+#include <utils/fileutils.h>
#include <vcsbase/vcsbaseconstants.h>
#include <QDir>
@@ -135,6 +136,18 @@ void ProjectWizardPage::changeEvent(QEvent *e)
}
}
+// Alphabetically, and files in sub-directories first
+static bool generatedFilePathLessThan(const QString &filePath1, const QString &filePath2)
+{
+ const bool filePath1HasDir = filePath1.contains(QLatin1Char('/'));
+ const bool filePath2HasDir = filePath2.contains(QLatin1Char('/'));
+
+ if (filePath1HasDir == filePath2HasDir)
+ return Utils::FileName::fromString(filePath1) < Utils::FileName::fromString(filePath2);
+ else
+ return filePath1HasDir;
+}
+
void ProjectWizardPage::setFilesDisplay(const QString &commonPath, const QStringList &files)
{
QString fileMessage;
@@ -143,15 +156,21 @@ void ProjectWizardPage::setFilesDisplay(const QString &commonPath, const QString
str << "<qt>"
<< (commonPath.isEmpty() ? tr("Files to be added:") : tr("Files to be added in"))
<< "<pre>";
+
+ QStringList formattedFiles;
if (commonPath.isEmpty()) {
- foreach(const QString &f, files)
- str << QDir::toNativeSeparators(f) << '\n';
+ formattedFiles = files;
} else {
str << QDir::toNativeSeparators(commonPath) << ":\n\n";
const int prefixSize = commonPath.size() + 1;
- foreach(const QString &f, files)
- str << QDir::toNativeSeparators(f.right(f.size() - prefixSize)) << '\n';
+ foreach (const QString &f, files)
+ formattedFiles.append(f.right(f.size() - prefixSize));
}
+ qSort(formattedFiles.begin(), formattedFiles.end(), generatedFilePathLessThan);
+
+ foreach (const QString &f, formattedFiles)
+ str << QDir::toNativeSeparators(f) << '\n';
+
str << "</pre>";
}
m_ui->filesLabel->setText(fileMessage);
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index 765b22b0e1..dad8503c5a 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -64,6 +64,7 @@ const char USE_CPP_DEBUGGER_KEY[] = "RunConfiguration.UseCppDebugger";
const char USE_QML_DEBUGGER_KEY[] = "RunConfiguration.UseQmlDebugger";
const char USE_QML_DEBUGGER_AUTO_KEY[] = "RunConfiguration.UseQmlDebuggerAuto";
const char QML_DEBUG_SERVER_PORT_KEY[] = "RunConfiguration.QmlDebugServerPort";
+const char USE_MULTIPROCESS_KEY[] = "RunConfiguration.UseMultiProcess";
// Function objects:
@@ -203,6 +204,7 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(RunConfiguration
m_useCppDebugger(true),
m_useQmlDebugger(AutoEnableQmlDebugger),
m_qmlDebugServerPort(Constants::QML_DEFAULT_DEBUG_SERVER_PORT),
+ m_useMultiProcess(false),
m_suppressDisplay(false),
m_suppressQmlDebuggingOptions(false),
m_suppressCppDebuggingOptions(false),
@@ -214,6 +216,7 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(DebuggerRunConfig
m_useCppDebugger(other->m_useCppDebugger),
m_useQmlDebugger(other->m_useQmlDebugger),
m_qmlDebugServerPort(other->m_qmlDebugServerPort),
+ m_useMultiProcess(other->m_useMultiProcess),
m_suppressDisplay(other->m_suppressDisplay),
m_suppressQmlDebuggingOptions(other->m_suppressQmlDebuggingOptions),
m_suppressCppDebuggingOptions(other->m_suppressCppDebuggingOptions),
@@ -266,6 +269,16 @@ void DebuggerRunConfigurationAspect::setQmllDebugServerPort(uint port)
m_qmlDebugServerPort = port;
}
+bool DebuggerRunConfigurationAspect::useMultiProcess() const
+{
+ return m_useMultiProcess;
+}
+
+void DebuggerRunConfigurationAspect::setUseMultiProcess(bool value)
+{
+ m_useMultiProcess = value;
+}
+
void DebuggerRunConfigurationAspect::suppressDisplay()
{
m_suppressDisplay = true;
@@ -318,6 +331,7 @@ QVariantMap DebuggerRunConfigurationAspect::toMap() const
map.insert(QLatin1String(USE_QML_DEBUGGER_KEY), m_useQmlDebugger == EnableQmlDebugger);
map.insert(QLatin1String(USE_QML_DEBUGGER_AUTO_KEY), m_useQmlDebugger == AutoEnableQmlDebugger);
map.insert(QLatin1String(QML_DEBUG_SERVER_PORT_KEY), m_qmlDebugServerPort);
+ map.insert(QLatin1String(USE_MULTIPROCESS_KEY), m_useMultiProcess);
return map;
}
@@ -330,6 +344,7 @@ void DebuggerRunConfigurationAspect::fromMap(const QVariantMap &map)
bool useQml = map.value(QLatin1String(USE_QML_DEBUGGER_KEY), false).toBool();
m_useQmlDebugger = useQml ? EnableQmlDebugger : DisableQmlDebugger;
}
+ m_useMultiProcess = map.value(QLatin1String(USE_MULTIPROCESS_KEY), false).toBool();
}
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index aa6981f0d8..978f01b99a 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -106,12 +106,14 @@ public:
QString displayName() const;
- void setUseQmlDebugger(bool value);
- void setUseCppDebugger(bool value);
bool useCppDebugger() const;
+ void setUseCppDebugger(bool value);
bool useQmlDebugger() const;
+ void setUseQmlDebugger(bool value);
uint qmlDebugServerPort() const;
void setQmllDebugServerPort(uint port);
+ bool useMultiProcess() const;
+ void setUseMultiProcess(bool on);
void suppressDisplay();
void suppressQmlDebuggingOptions();
void suppressCppDebuggingOptions();
@@ -130,6 +132,8 @@ public:
bool m_useCppDebugger;
QmlDebuggerStatus m_useQmlDebugger;
uint m_qmlDebugServerPort;
+ bool m_useMultiProcess;
+
bool m_suppressDisplay;
bool m_suppressQmlDebuggingOptions;
bool m_suppressCppDebuggingOptions;
@@ -174,7 +178,7 @@ public:
virtual ProjectExplorer::Abi abi() const;
signals:
- void isEnabledChanged(bool value);
+ void enabledChanged();
protected:
RunConfiguration(Target *parent, const QString &id);
diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp
index 5cc125d8ee..4ee674d3ef 100644
--- a/src/plugins/projectexplorer/target.cpp
+++ b/src/plugins/projectexplorer/target.cpp
@@ -129,6 +129,20 @@ void Target::changeBuildConfigurationEnabled()
emit buildConfigurationEnabledChanged();
}
+void Target::changeDeployConfigurationEnabled()
+{
+ DeployConfiguration *dc = qobject_cast<DeployConfiguration *>(sender());
+ if (dc == activeDeployConfiguration())
+ emit deployConfigurationEnabledChanged();
+}
+
+void Target::changeRunConfigurationEnabled()
+{
+ RunConfiguration *rc = qobject_cast<RunConfiguration *>(sender());
+ if (rc == activeRunConfiguration())
+ emit runConfigurationEnabledChanged();
+}
+
Project *Target::project() const
{
return static_cast<Project *>(parent());
@@ -243,6 +257,8 @@ void Target::addDeployConfiguration(DeployConfiguration *dc)
// add it
d->m_deployConfigurations.push_back(dc);
+ connect(dc, SIGNAL(enabledChanged()), this, SLOT(changeDeployConfigurationEnabled()));
+
emit addedDeployConfiguration(dc);
if (!d->m_activeDeployConfiguration)
@@ -293,6 +309,7 @@ void Target::setActiveDeployConfiguration(DeployConfiguration *dc)
dc != d->m_activeDeployConfiguration)) {
d->m_activeDeployConfiguration = dc;
emit activeDeployConfigurationChanged(d->m_activeDeployConfiguration);
+ emit deployConfigurationEnabledChanged();
}
}
@@ -341,6 +358,9 @@ void Target::addRunConfiguration(RunConfiguration* runConfiguration)
runConfiguration->setDisplayName(configurationDisplayName);
d->m_runConfigurations.push_back(runConfiguration);
+
+ connect(runConfiguration, SIGNAL(enabledChanged()), this, SLOT(changeRunConfigurationEnabled()));
+
emit addedRunConfiguration(runConfiguration);
if (!activeRunConfiguration())
@@ -376,6 +396,7 @@ void Target::setActiveRunConfiguration(RunConfiguration* configuration)
configuration != d->m_activeRunConfiguration)) {
d->m_activeRunConfiguration = configuration;
emit activeRunConfigurationChanged(d->m_activeRunConfiguration);
+ emit runConfigurationEnabledChanged();
}
}
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index 2bfb2bf584..d15c1b0420 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -112,7 +112,6 @@ public:
virtual QList<ToolChain *> possibleToolChains(BuildConfiguration *) const;
virtual ToolChain *preferredToolChain(BuildConfiguration *) const;
-
virtual QVariantMap toMap() const;
signals:
@@ -123,8 +122,6 @@ signals:
// TODO clean up signal names
// might be better to also have aboutToRemove signals
- void runConfigurationsEnabledStateChanged();
-
void removedRunConfiguration(ProjectExplorer::RunConfiguration *);
void addedRunConfiguration(ProjectExplorer::RunConfiguration *);
void activeRunConfigurationChanged(ProjectExplorer::RunConfiguration *);
@@ -141,9 +138,11 @@ signals:
/// environmentChanged() or if the active build configuration changes
void environmentChanged();
- /// convenience signal, emitted if either the active buildconfiguration emits
+ /// convenience signal, emitted if either the active configuration emits
/// enabledChanged() or if the active build configuration changes
void buildConfigurationEnabledChanged();
+ void deployConfigurationEnabledChanged();
+ void runConfigurationEnabledChanged();
protected:
Target(Project *parent, const QString &id);
@@ -155,6 +154,8 @@ protected:
private slots:
void changeEnvironment();
void changeBuildConfigurationEnabled();
+ void changeDeployConfigurationEnabled();
+ void changeRunConfigurationEnabled();
private:
TargetPrivate *d;
diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp
index 1223b46b20..39ce6a4bd1 100644
--- a/src/plugins/projectexplorer/taskhub.cpp
+++ b/src/plugins/projectexplorer/taskhub.cpp
@@ -144,6 +144,11 @@ void TaskHub::taskMarkClicked(unsigned int id)
emit showTask(id);
}
+void TaskHub::showTaskInEditor(unsigned int id)
+{
+ emit openTask(id);
+}
+
void TaskHub::setCategoryVisibility(const Core::Id &categoryId, bool visible)
{
emit categoryVisibilityChanged(categoryId, visible);
diff --git a/src/plugins/projectexplorer/taskhub.h b/src/plugins/projectexplorer/taskhub.h
index e48d3f507c..66370c30ff 100644
--- a/src/plugins/projectexplorer/taskhub.h
+++ b/src/plugins/projectexplorer/taskhub.h
@@ -55,6 +55,7 @@ public:
void updateTaskFileName(unsigned int id, const QString &fileName);
void updateTaskLineNumber(unsigned int id, int line);
void taskMarkClicked(unsigned int id);
+ void showTaskInEditor(unsigned int id);
void setCategoryVisibility(const Core::Id &categoryId, bool visible);
void popup(bool withFocus);
@@ -71,6 +72,7 @@ signals:
void categoryVisibilityChanged(const Core::Id &categoryId, bool visible);
void popupRequested(bool withFocus);
void showTask(unsigned int id);
+ void openTask(unsigned int id);
private:
const QIcon m_errorIcon;
const QIcon m_warningIcon;
diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp
index 3858c2661e..41dea75060 100644
--- a/src/plugins/projectexplorer/taskwindow.cpp
+++ b/src/plugins/projectexplorer/taskwindow.cpp
@@ -309,6 +309,8 @@ TaskWindow::TaskWindow(TaskHub *taskhub) : d(new TaskWindowPrivate)
this, SLOT(popup(bool)));
connect(d->m_taskHub, SIGNAL(showTask(uint)),
this, SLOT(showTask(uint)));
+ connect(d->m_taskHub, SIGNAL(openTask(uint)),
+ this, SLOT(openTask(uint)));
}
TaskWindow::~TaskWindow()
@@ -408,6 +410,14 @@ void TaskWindow::showTask(unsigned int id)
popup(false);
}
+void TaskWindow::openTask(unsigned int id)
+{
+ int sourceRow = d->m_model->rowForId(id);
+ QModelIndex sourceIdx = d->m_model->index(sourceRow, 0);
+ QModelIndex filterIdx = d->m_filter->mapFromSource(sourceIdx);
+ triggerDefaultHandler(filterIdx);
+}
+
void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
{
if (!index.isValid())
diff --git a/src/plugins/projectexplorer/taskwindow.h b/src/plugins/projectexplorer/taskwindow.h
index a83789b5db..1866d9c05a 100644
--- a/src/plugins/projectexplorer/taskwindow.h
+++ b/src/plugins/projectexplorer/taskwindow.h
@@ -92,6 +92,7 @@ private slots:
void updatedTaskFileName(unsigned int id, const QString &fileName);
void updatedTaskLineNumber(unsigned int id, int line);
void showTask(unsigned int id);
+ void openTask(unsigned int id);
void clearTasks(const Core::Id &categoryId);
void setCategoryVisibility(const Core::Id &categoryId, bool visible);
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h
index ec5cbb9af3..87d192e24a 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.h
+++ b/src/plugins/qmldesigner/qmldesignerplugin.h
@@ -65,6 +65,7 @@ class DesignModeContext;
class BauhausPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlDesigner.json")
public:
BauhausPlugin();
diff --git a/src/plugins/qmljseditor/qmlfilewizard.cpp b/src/plugins/qmljseditor/qmlfilewizard.cpp
index 5456908c8e..36911ddca6 100644
--- a/src/plugins/qmljseditor/qmlfilewizard.cpp
+++ b/src/plugins/qmljseditor/qmlfilewizard.cpp
@@ -59,12 +59,10 @@ Core::GeneratedFiles QmlFileWizard::generateFilesFromPath(const QString &path,
return Core::GeneratedFiles() << file;
}
-QString QmlFileWizard::fileContents(const QString &fileName) const
+QString QmlFileWizard::fileContents(const QString &) const
{
- const QString baseName = QFileInfo(fileName).completeBaseName();
QString contents;
QTextStream str(&contents);
-// str << CppTools::AbstractEditorSupport::licenseTemplate();
// 100:62 is the 'golden ratio'
str << QLatin1String("// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5\n")
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index 78ea9cff7b..b2f303a0e4 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -1387,11 +1387,6 @@ TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const Q
return Link();
}
-void QmlJSTextEditorWidget::followSymbolUnderCursor()
-{
- openLink(findLinkAt(textCursor()));
-}
-
void QmlJSTextEditorWidget::findUsages()
{
m_findReferences->findUsages(editorDocument()->fileName(), textCursor().position());
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index b97f327083..566dc0aa60 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -176,7 +176,6 @@ public slots:
void reparseDocumentNow();
void updateSemanticInfo();
void updateSemanticInfoNow();
- void followSymbolUnderCursor();
void findUsages();
void renameUsages();
void showContextPane();
diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h
index dda05bb967..34dccae4f4 100644
--- a/src/plugins/qmljseditor/qmljseditorconstants.h
+++ b/src/plugins/qmljseditor/qmljseditorconstants.h
@@ -51,7 +51,6 @@ const char TASK_SEARCH[] = "QmlJSEditor.TaskSearch";
const char SETTINGS_CATEGORY_QML[] = "J.QtQuick";
const char SETTINGS_TR_CATEGORY_QML[] = QT_TRANSLATE_NOOP("QmlJSEditor", "Qt Quick");
-const char FOLLOW_SYMBOL_UNDER_CURSOR[] = "QmlJSEditor.FollowSymbolUnderCursor";
const char FIND_USAGES[] = "QmlJSEditor.FindUsages";
const char RENAME_USAGES[] = "QmlJSEditor.RenameUsages";
const char RUN_SEMANTIC_SCAN[] = "QmlJSEditor.RunSemanticScan";
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index 6795134d62..5faae4381f 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -64,7 +64,7 @@
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/taskhub.h>
-#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/projectexplorer.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/textfilewizard.h>
@@ -84,6 +84,7 @@
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
using namespace QmlJSEditor::Constants;
+using namespace ProjectExplorer;
enum {
QUICKFIX_INTERVAL = 20
@@ -173,7 +174,8 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
m_actionHandler = new TextEditor::TextEditorActionHandler(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
TextEditor::TextEditorActionHandler::Format
| TextEditor::TextEditorActionHandler::UnCommentSelection
- | TextEditor::TextEditorActionHandler::UnCollapseAll);
+ | TextEditor::TextEditorActionHandler::UnCollapseAll
+ | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
m_actionHandler->initializeActions();
Core::ActionManager *am = Core::ICore::actionManager();
@@ -184,10 +186,7 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
qmlToolsMenu->addAction(createSeparator(am, this, globalContext, QmlJSEditor::Constants::SEPARATOR3));
Core::Command *cmd;
- QAction *followSymbolUnderCursorAction = new QAction(tr("Follow Symbol Under Cursor"), this);
- cmd = am->registerAction(followSymbolUnderCursorAction, Constants::FOLLOW_SYMBOL_UNDER_CURSOR, context);
- cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
- connect(followSymbolUnderCursorAction, SIGNAL(triggered()), this, SLOT(followSymbolUnderCursor()));
+ cmd = am->command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR);
contextMenu->addAction(cmd);
qmlToolsMenu->addAction(cmd);
@@ -266,8 +265,7 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
void QmlJSEditorPlugin::extensionsInitialized()
{
- ProjectExplorer::TaskHub *taskHub =
- ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
+ TaskHub *taskHub = ProjectExplorerPlugin::instance()->taskHub();
taskHub->addCategory(Constants::TASK_CATEGORY_QML, tr("QML"));
taskHub->addCategory(Constants::TASK_CATEGORY_QML_ANALYSIS, tr("QML Analysis"), false);
}
@@ -294,14 +292,6 @@ Utils::JsonSchemaManager *QmlJSEditorPlugin::jsonManager() const
return m_jsonManager.data();
}
-void QmlJSEditorPlugin::followSymbolUnderCursor()
-{
- Core::EditorManager *em = Core::EditorManager::instance();
-
- if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget()))
- editor->followSymbolUnderCursor();
-}
-
void QmlJSEditorPlugin::findUsages()
{
Core::EditorManager *em = Core::EditorManager::instance();
@@ -380,7 +370,7 @@ void QmlJSEditorPlugin::currentEditorChanged(Core::IEditor *editor)
void QmlJSEditorPlugin::runSemanticScan()
{
m_qmlTaskManager->updateSemanticMessagesNow();
- ProjectExplorer::TaskHub *hub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
+ TaskHub *hub = ProjectExplorerPlugin::instance()->taskHub();
hub->setCategoryVisibility(Constants::TASK_CATEGORY_QML_ANALYSIS, true);
hub->popup(false);
}
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h
index 1633f0fd84..29e22f6b32 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.h
+++ b/src/plugins/qmljseditor/qmljseditorplugin.h
@@ -79,6 +79,7 @@ class QmlTaskManager;
class QmlJSEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlJSEditor.json")
public:
QmlJSEditorPlugin();
@@ -99,7 +100,6 @@ public:
Utils::JsonSchemaManager *jsonManager() const;
public Q_SLOTS:
- void followSymbolUnderCursor();
void findUsages();
void renameUsages();
void reformatFile();
diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp
index 4a238d5b8d..ed4e636af1 100644
--- a/src/plugins/qmljseditor/qmltaskmanager.cpp
+++ b/src/plugins/qmljseditor/qmltaskmanager.cpp
@@ -34,7 +34,7 @@
#include "qmljseditorconstants.h"
#include <coreplugin/idocument.h>
-#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/taskhub.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljscontext.h>
@@ -57,7 +57,7 @@ QmlTaskManager::QmlTaskManager(QObject *parent) :
m_taskHub(0),
m_updatingSemantic(false)
{
- m_taskHub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
+ m_taskHub = ProjectExplorer::ProjectExplorerPlugin::instance()->taskHub();
// displaying results incrementally leads to flickering
// connect(&m_messageCollector, SIGNAL(resultsReadyAt(int,int)),
diff --git a/src/plugins/qmljsinspector/images/color-picker-small-hicontrast.png b/src/plugins/qmljsinspector/images/color-picker-small-hicontrast.png
deleted file mode 100644
index 95b88ac8f0..0000000000
--- a/src/plugins/qmljsinspector/images/color-picker-small-hicontrast.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/qmljsinspector/images/color-picker-small.png b/src/plugins/qmljsinspector/images/color-picker-small.png
deleted file mode 100644
index 026c31b3e1..0000000000
--- a/src/plugins/qmljsinspector/images/color-picker-small.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/qmljsinspector/images/color-picker.png b/src/plugins/qmljsinspector/images/color-picker.png
deleted file mode 100644
index 73d9ae3dfc..0000000000
--- a/src/plugins/qmljsinspector/images/color-picker.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.cpp b/src/plugins/qmljsinspector/qmljsclientproxy.cpp
index 418de0119f..5c8b3104a4 100644
--- a/src/plugins/qmljsinspector/qmljsclientproxy.cpp
+++ b/src/plugins/qmljsinspector/qmljsclientproxy.cpp
@@ -53,13 +53,10 @@ ClientProxy::ClientProxy(Debugger::QmlAdapter *adapter, QObject *parent)
, m_adapter(adapter)
, m_engineClient(0)
, m_inspectorClient(0)
- , m_engineQuery(0)
- , m_contextQuery(0)
+ , m_engineQueryId(0)
+ , m_contextQueryId(0)
, m_isConnected(false)
{
- m_requestObjectsTimer.setSingleShot(true);
- m_requestObjectsTimer.setInterval(3000);
- connect(&m_requestObjectsTimer, SIGNAL(timeout()), this, SLOT(refreshObjectTree()));
connectToServer();
}
@@ -71,22 +68,29 @@ ClientProxy::~ClientProxy()
void ClientProxy::connectToServer()
{
- m_engineClient = new QDeclarativeEngineDebug(m_adapter.data()->connection(), this);
+ QmlEngineDebugClient *client1 = new QDeclarativeEngineClient(
+ m_adapter.data()->connection());
+ QmlEngineDebugClient *client2 = new QmlDebuggerClient(
+ m_adapter.data()->connection());
- connect(m_engineClient, SIGNAL(newObjects()), this, SLOT(newObjects()));
- connect(m_engineClient, SIGNAL(statusChanged(QDeclarativeDebugClient::Status)),
- this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
- connect(m_engineClient, SIGNAL(statusChanged(QDeclarativeDebugClient::Status)),
- this, SLOT(engineClientStatusChanged(QDeclarativeDebugClient::Status)));
+ connect(client1, SIGNAL(newStatus(QDeclarativeDebugClient::Status)),
+ SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
+ connect(client1, SIGNAL(newStatus(QDeclarativeDebugClient::Status)),
+ SLOT(engineClientStatusChanged(QDeclarativeDebugClient::Status)));
+
+ connect(client2, SIGNAL(newStatus(QDeclarativeDebugClient::Status)),
+ SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
+ connect(client2, SIGNAL(newStatus(QDeclarativeDebugClient::Status)),
+ SLOT(engineClientStatusChanged(QDeclarativeDebugClient::Status)));
- m_inspectorClient = new QmlJSInspectorClient(m_adapter.data()->connection(), this);
+ m_inspectorClient =
+ new QmlJSInspectorClient(m_adapter.data()->connection(), this);
- connect(m_inspectorClient, SIGNAL(connectedStatusChanged(QDeclarativeDebugClient::Status)),
- this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
+ connect(m_inspectorClient,
+ SIGNAL(connectedStatusChanged(QDeclarativeDebugClient::Status)),
+ this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
connect(m_inspectorClient, SIGNAL(currentObjectsChanged(QList<int>)),
SLOT(onCurrentObjectsChanged(QList<int>)));
- connect(m_inspectorClient, SIGNAL(colorPickerActivated()),
- SIGNAL(colorPickerActivated()));
connect(m_inspectorClient, SIGNAL(zoomToolActivated()),
SIGNAL(zoomToolActivated()));
connect(m_inspectorClient, SIGNAL(selectToolActivated()),
@@ -103,8 +107,6 @@ void ClientProxy::connectToServer()
SIGNAL(showAppOnTopChanged(bool)));
connect(m_inspectorClient, SIGNAL(reloaded()), this,
SIGNAL(serverReloaded()));
- connect(m_inspectorClient, SIGNAL(selectedColorChanged(QColor)),
- SIGNAL(selectedColorChanged(QColor)));
connect(m_inspectorClient, SIGNAL(logActivity(QString,QString)),
m_adapter.data(), SLOT(logServiceActivity(QString,QString)));
@@ -127,42 +129,65 @@ void ClientProxy::clientStatusChanged(QDeclarativeDebugClient::Status status)
void ClientProxy::engineClientStatusChanged(QDeclarativeDebugClient::Status status)
{
if (status == QDeclarativeDebugClient::Enabled) {
- m_adapter.data()->setEngineDebugClient(qobject_cast<QDeclarativeEngineDebug *>(sender()));
+ m_engineClient = qobject_cast<QmlEngineDebugClient *>(sender());
+ connect(m_engineClient, SIGNAL(newObjects()), this, SLOT(newObjects()));
+ connect(m_engineClient, SIGNAL(result(quint32,QVariant,QByteArray)),
+ SLOT(onResult(quint32,QVariant,QByteArray)));
+ connect(m_engineClient, SIGNAL(valueChanged(int,QByteArray,QVariant)),
+ SLOT(objectWatchTriggered(int,QByteArray,QVariant)));
+ m_adapter.data()->setEngineDebugClient(m_engineClient);
+ updateConnected();
}
}
void ClientProxy::refreshObjectTree()
{
- if (!m_contextQuery) {
- m_requestObjectsTimer.stop();
- qDeleteAll(m_objectTreeQuery);
- m_objectTreeQuery.clear();
+ if (!m_contextQueryId) {
+ m_objectTreeQueryIds.clear();
queryEngineContext(m_engines.value(0).debugId());
}
}
-void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds, bool requestIfNeeded)
+void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds,
+ bool requestIfNeeded)
{
- QList<QDeclarativeDebugObjectReference> selectedItems;
-
+ QList<QmlDebugObjectReference> selectedItems;
+ m_fetchCurrentObjects.clear();
+ m_fetchCurrentObjectsQueryIds.clear();
foreach (int debugId, debugIds) {
- QDeclarativeDebugObjectReference ref = objectReferenceForId(debugId);
- if (ref.debugId() != -1) {
+ QmlDebugObjectReference ref = objectReferenceForId(debugId);
+ if (ref.debugId() != -1 && !ref.needsMoreData()) {
selectedItems << ref;
} else if (requestIfNeeded) {
- // ### FIXME right now, there's no way in the protocol to
- // a) get some item and know its parent (although that's possible
- // by adding it to a separate plugin)
- // b) add children to part of an existing tree.
- // So the only choice that remains is to update the complete
- // tree when we have an unknown debug id.
- // break;
+ m_fetchCurrentObjectsQueryIds << fetchContextObject(
+ QmlDebugObjectReference(debugId));
+
}
}
emit selectedItemsChanged(selectedItems);
}
+void ClientProxy::onCurrentObjectsFetched(quint32 queryId, const QVariant &result)
+{
+ m_fetchCurrentObjectsQueryIds.removeOne(queryId);
+ QmlDebugObjectReference obj = qvariant_cast<QmlDebugObjectReference>(result);
+ m_fetchCurrentObjects.push_front(obj);
+
+ //If this is not a root object, check if we have the parent
+ QmlDebugObjectReference parent = objectReferenceForId(obj.parentId());
+ if (obj.parentId() != -1 && (parent.debugId() == -1 || parent.needsMoreData())) {
+ m_fetchCurrentObjectsQueryIds << fetchContextObject(
+ QmlDebugObjectReference(obj.parentId()));
+ return;
+ }
+
+ foreach (const QmlDebugObjectReference &o, m_fetchCurrentObjects)
+ addObjectToTree(o);
+ emit selectedItemsChanged(QList<QmlDebugObjectReference>() <<
+ m_fetchCurrentObjects.last());
+}
+
void ClientProxy::setSelectedItemsByDebugId(const QList<int> &debugIds)
{
if (!isConnected())
@@ -171,12 +196,13 @@ void ClientProxy::setSelectedItemsByDebugId(const QList<int> &debugIds)
m_inspectorClient->setCurrentObjects(debugIds);
}
-void ClientProxy::setSelectedItemsByObjectId(const QList<QDeclarativeDebugObjectReference> &objectRefs)
+void ClientProxy::setSelectedItemsByObjectId(
+ const QList<QmlDebugObjectReference> &objectRefs)
{
if (isConnected()) {
QList<int> debugIds;
- foreach (const QDeclarativeDebugObjectReference &ref, objectRefs) {
+ foreach (const QmlDebugObjectReference &ref, objectRefs) {
debugIds << ref.debugId();
}
@@ -184,14 +210,26 @@ void ClientProxy::setSelectedItemsByObjectId(const QList<QDeclarativeDebugObject
}
}
-QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId) const
+void ClientProxy::addObjectToTree(const QmlDebugObjectReference &obj)
+{
+ int count = m_rootObjects.count();
+ for (int i = 0; i < count; i++) {
+ if (m_rootObjects[i].insertObjectInTree(obj)) {
+ buildDebugIdHashRecursive(obj);
+ emit objectTreeUpdated();
+ break;
+ }
+ }
+}
+
+QmlDebugObjectReference ClientProxy::objectReferenceForId(int debugId) const
{
- foreach (const QDeclarativeDebugObjectReference& it, m_rootObjects) {
- QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, it);
+ foreach (const QmlDebugObjectReference& it, m_rootObjects) {
+ QmlDebugObjectReference result = objectReferenceForId(debugId, it);
if (result.debugId() == debugId)
return result;
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
void ClientProxy::log(LogDirection direction, const QString &message)
@@ -208,71 +246,76 @@ void ClientProxy::log(LogDirection direction, const QString &message)
m_adapter.data()->logServiceActivity("QDeclarativeDebug", msg);
}
-QList<QDeclarativeDebugObjectReference> QmlJSInspector::Internal::ClientProxy::rootObjectReference() const
+QList<QmlDebugObjectReference>
+QmlJSInspector::Internal::ClientProxy::rootObjectReference() const
{
return m_rootObjects;
}
-QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId,
- const QDeclarativeDebugObjectReference &objectRef) const
+QmlDebugObjectReference
+ClientProxy::objectReferenceForId(int debugId,
+ const QmlDebugObjectReference &objectRef) const
{
if (objectRef.debugId() == debugId)
return objectRef;
- foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) {
- QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, child);
+ foreach (const QmlDebugObjectReference &child, objectRef.children()) {
+ QmlDebugObjectReference result = objectReferenceForId(debugId, child);
if (result.debugId() == debugId)
return result;
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
-QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(const QString &objectId) const
+QmlDebugObjectReference ClientProxy::objectReferenceForId(
+ const QString &objectId) const
{
if (!objectId.isEmpty() && objectId[0].isLower()) {
- const QList<QDeclarativeDebugObjectReference> refs = objectReferences();
- foreach (const QDeclarativeDebugObjectReference &ref, refs) {
+ const QList<QmlDebugObjectReference> refs = objectReferences();
+ foreach (const QmlDebugObjectReference &ref, refs) {
if (ref.idString() == objectId)
return ref;
}
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
-QDeclarativeDebugObjectReference ClientProxy::objectReferenceForLocation(const int line, const int column) const
+QmlDebugObjectReference ClientProxy::objectReferenceForLocation(
+ const int line, const int column) const
{
- const QList<QDeclarativeDebugObjectReference> refs = objectReferences();
- foreach (const QDeclarativeDebugObjectReference &ref, refs) {
+ const QList<QmlDebugObjectReference> refs = objectReferences();
+ foreach (const QmlDebugObjectReference &ref, refs) {
if (ref.source().lineNumber() == line && ref.source().columnNumber() == column)
return ref;
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
-QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences() const
+QList<QmlDebugObjectReference> ClientProxy::objectReferences() const
{
- QList<QDeclarativeDebugObjectReference> result;
- foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects) {
+ QList<QmlDebugObjectReference> result;
+ foreach (const QmlDebugObjectReference &it, m_rootObjects) {
result.append(objectReferences(it));
}
return result;
}
-QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QDeclarativeDebugObjectReference &objectRef) const
+QList<QmlDebugObjectReference>
+ClientProxy::objectReferences(const QmlDebugObjectReference &objectRef) const
{
- QList<QDeclarativeDebugObjectReference> result;
+ QList<QmlDebugObjectReference> result;
result.append(objectRef);
- foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) {
+ foreach (const QmlDebugObjectReference &child, objectRef.children()) {
result.append(objectReferences(child));
}
return result;
}
-bool ClientProxy::setBindingForObject(int objectDebugId,
+quint32 ClientProxy::setBindingForObject(int objectDebugId,
const QString &propertyName,
const QVariant &value,
bool isLiteralValue,
@@ -288,17 +331,23 @@ bool ClientProxy::setBindingForObject(int objectDebugId,
if (!isConnected())
return false;
- log(LogSend, QString("SET_BINDING %1 %2 %3 %4").arg(QString::number(objectDebugId), propertyName, value.toString(), QString(isLiteralValue ? "true" : "false")));
+ log(LogSend, QString("SET_BINDING %1 %2 %3 %4").arg(
+ QString::number(objectDebugId), propertyName, value.toString(),
+ QString(isLiteralValue ? "true" : "false")));
- bool result = m_engineClient->setBindingForObject(objectDebugId, propertyName, value.toString(), isLiteralValue, source, line);
+ quint32 queryId = m_engineClient->setBindingForObject(
+ objectDebugId, propertyName, value.toString(), isLiteralValue,
+ source, line);
- if (!result)
+ if (!queryId)
log(LogSend, QString("failed!"));
- return result;
+ return queryId;
}
-bool ClientProxy::setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody)
+quint32 ClientProxy::setMethodBodyForObject(int objectDebugId,
+ const QString &methodName,
+ const QString &methodBody)
{
if (objectDebugId == -1)
return false;
@@ -306,17 +355,20 @@ bool ClientProxy::setMethodBodyForObject(int objectDebugId, const QString &metho
if (!isConnected())
return false;
- log(LogSend, QString("SET_METHOD_BODY %1 %2 %3").arg(QString::number(objectDebugId), methodName, methodBody));
+ log(LogSend, QString("SET_METHOD_BODY %1 %2 %3").arg(
+ QString::number(objectDebugId), methodName, methodBody));
- bool result = m_engineClient->setMethodBody(objectDebugId, methodName, methodBody);
+ quint32 queryId = m_engineClient->setMethodBody(
+ objectDebugId, methodName, methodBody);
- if (!result)
+ if (!queryId)
log(LogSend, QString("failed!"));
- return result;
+ return queryId;
}
-bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& propertyName)
+quint32 ClientProxy::resetBindingForObject(int objectDebugId,
+ const QString& propertyName)
{
if (objectDebugId == -1)
return false;
@@ -324,17 +376,20 @@ bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& proper
if (!isConnected())
return false;
- log(LogSend, QString("RESET_BINDING %1 %2").arg(QString::number(objectDebugId), propertyName));
+ log(LogSend, QString("RESET_BINDING %1 %2").arg(
+ QString::number(objectDebugId), propertyName));
- bool result = m_engineClient->resetBindingForObject(objectDebugId, propertyName);
+ quint32 queryId = m_engineClient->resetBindingForObject(
+ objectDebugId, propertyName);
- if (!result)
+ if (!queryId)
log(LogSend, QString("failed!"));
- return result;
+ return queryId;
}
-QDeclarativeDebugExpressionQuery *ClientProxy::queryExpressionResult(int objectDebugId, const QString &expr)
+quint32 ClientProxy::queryExpressionResult(int objectDebugId,
+ const QString &expr)
{
if (objectDebugId == -1)
return 0;
@@ -346,13 +401,13 @@ QDeclarativeDebugExpressionQuery *ClientProxy::queryExpressionResult(int objectD
if (m_adapter)
block = m_adapter.data()->disableJsDebugging(true);
- log(LogSend, QString("EVAL_EXPRESSION %1 %2").arg(QString::number(objectDebugId), expr));
- QDeclarativeDebugExpressionQuery *query
- = m_engineClient->queryExpressionResult(objectDebugId, expr, m_engineClient);
+ log(LogSend, QString("EVAL_EXPRESSION %1 %2").arg(
+ QString::number(objectDebugId), expr));
+ quint32 queryId = m_engineClient->queryExpressionResult(objectDebugId, expr);
if (m_adapter)
m_adapter.data()->disableJsDebugging(block);
- return query;
+ return queryId;
}
void ClientProxy::clearComponentCache()
@@ -370,32 +425,34 @@ bool ClientProxy::addObjectWatch(int objectDebugId)
return false;
// already set
- if (m_objectWatches.keys().contains(objectDebugId))
+ if (m_objectWatches.contains(objectDebugId))
return true;
- QDeclarativeDebugObjectReference ref = objectReferenceForId(objectDebugId);
+ QmlDebugObjectReference ref = objectReferenceForId(objectDebugId);
if (ref.debugId() != objectDebugId)
return false;
// is flooding the debugging output log!
// log(LogSend, QString("WATCH_PROPERTY %1").arg(objectDebugId));
- QDeclarativeDebugWatch *watch = m_engineClient->addWatch(ref, m_engineClient);
- m_objectWatches.insert(objectDebugId, watch);
-
- connect(watch,SIGNAL(valueChanged(QByteArray,QVariant)),this,SLOT(objectWatchTriggered(QByteArray,QVariant)));
+ if (m_engineClient->addWatch(ref))
+ m_objectWatches.append(objectDebugId);
return false;
}
-void ClientProxy::objectWatchTriggered(const QByteArray &propertyName, const QVariant &propertyValue)
+bool ClientProxy::isObjectBeingWatched(int objectDebugId)
{
- // is flooding the debugging output log!
- // log(LogReceive, QString("UPDATE_WATCH %1 %2").arg(QString::fromAscii(propertyName), propertyValue.toString()));
+ return m_objectWatches.contains(objectDebugId);
+}
- QDeclarativeDebugWatch *watch = dynamic_cast<QDeclarativeDebugWatch *>(QObject::sender());
- if (watch)
- emit propertyChanged(watch->objectDebugId(),propertyName, propertyValue);
+
+void ClientProxy::objectWatchTriggered(int objectDebugId,
+ const QByteArray &propertyName,
+ const QVariant &propertyValue)
+{
+ if (m_objectWatches.contains(objectDebugId))
+ emit propertyChanged(objectDebugId, propertyName, propertyValue);
}
bool ClientProxy::removeObjectWatch(int objectDebugId)
@@ -403,29 +460,19 @@ bool ClientProxy::removeObjectWatch(int objectDebugId)
if (objectDebugId == -1)
return false;
- if (!m_objectWatches.keys().contains(objectDebugId))
+ if (!m_objectWatches.contains(objectDebugId))
return false;
if (!isConnected())
return false;
- QDeclarativeDebugWatch *watch = m_objectWatches.value(objectDebugId);
- disconnect(watch,SIGNAL(valueChanged(QByteArray,QVariant)), this, SLOT(objectWatchTriggered(QByteArray,QVariant)));
-
- // is flooding the debugging output log!
- // log(LogSend, QString("NO_WATCH %1").arg(QString::number(objectDebugId)));
-
- m_engineClient->removeWatch(watch);
- delete watch;
- m_objectWatches.remove(objectDebugId);
-
-
+ m_objectWatches.removeOne(objectDebugId);
return true;
}
void ClientProxy::removeAllObjectWatches()
{
- foreach (int watchedObject, m_objectWatches.keys())
+ foreach (int watchedObject, m_objectWatches)
removeObjectWatch(watchedObject);
}
@@ -437,87 +484,94 @@ void ClientProxy::queryEngineContext(int id)
if (!isConnected())
return;
- if (m_contextQuery) {
- delete m_contextQuery;
- m_contextQuery = 0;
- }
+ if (m_contextQueryId)
+ m_contextQueryId = 0;
log(LogSend, QString("LIST_OBJECTS %1").arg(QString::number(id)));
- m_contextQuery = m_engineClient->queryRootContexts(QDeclarativeDebugEngineReference(id),
- m_engineClient);
- if (!m_contextQuery->isWaiting())
- contextChanged();
- else
- connect(m_contextQuery, SIGNAL(stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State)),
- this, SLOT(contextChanged()));
+ m_contextQueryId = m_engineClient->queryRootContexts(QmlDebugEngineReference(id));
}
-void ClientProxy::contextChanged()
+void ClientProxy::contextChanged(const QVariant &value)
{
- log(LogReceive, QString("LIST_OBJECTS_R"));
- if (m_contextQuery) {
- m_rootObjects.clear();
- QDeclarativeDebugContextReference rootContext = m_contextQuery->rootContext();
- delete m_contextQuery;
- m_contextQuery = 0;
- qDeleteAll(m_objectTreeQuery);
- m_objectTreeQuery.clear();
- m_requestObjectsTimer.stop();
-
- fetchContextObjectRecursive(rootContext);
+ if (m_contextQueryId) {
+ m_contextQueryId = 0;
+ emit rootContext(value);
}
}
-void ClientProxy::fetchContextObjectRecursive(const QDeclarativeDebugContextReference& context)
+quint32 ClientProxy::fetchContextObject(const QmlDebugObjectReference& obj)
{
- if (!isConnected())
- return;
-
- foreach (const QDeclarativeDebugObjectReference & obj, context.objects()) {
+ log(LogSend, QString("FETCH_OBJECT %1").arg(obj.idString()));
- log(LogSend, QString("FETCH_OBJECT %1").arg(obj.idString()));
+ return m_engineClient->queryObject(obj);
+}
- QDeclarativeDebugObjectQuery* query
- = m_engineClient->queryObjectRecursive(obj, m_engineClient);
- if (!query->isWaiting()) {
- query->deleteLater(); //ignore errors;
- } else {
- m_objectTreeQuery << query;
- connect(query,
- SIGNAL(stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State)),
- SLOT(objectTreeFetched(QmlJsDebugClient::QDeclarativeDebugQuery::State)));
- }
+void ClientProxy::fetchContextObjectRecursive(
+ const QmlDebugContextReference& context, bool clear)
+{
+ if (!isConnected())
+ return;
+ if (clear) {
+ m_rootObjects.clear();
+ m_objectTreeQueryIds.clear();
+ }
+ foreach (const QmlDebugObjectReference & obj, context.objects()) {
+ quint32 queryId = fetchContextObject(obj);
+ if (queryId)
+ m_objectTreeQueryIds << queryId;
}
- foreach (const QDeclarativeDebugContextReference& child, context.contexts()) {
- fetchContextObjectRecursive(child);
+ foreach (const QmlDebugContextReference& child, context.contexts()) {
+ fetchContextObjectRecursive(child, false);
}
}
-
-void ClientProxy::objectTreeFetched(QmlJsDebugClient::QDeclarativeDebugQuery::State state)
+void ClientProxy::insertObjectInTreeIfNeeded(const QmlDebugObjectReference &object)
{
- QDeclarativeDebugObjectQuery *query = qobject_cast<QDeclarativeDebugObjectQuery *>(sender());
- if (!query || state == QDeclarativeDebugQuery::Error) {
- delete query;
+ if (!m_rootObjects.contains(object))
return;
+ int count = m_rootObjects.count();
+ for (int i = 0; i < count; i++) {
+ if (m_rootObjects[i].parentId() < 0 && m_rootObjects[i].insertObjectInTree(object)) {
+ m_rootObjects.removeOne(object);
+ break;
+ }
}
+}
- log(LogReceive, QString("FETCH_OBJECT_R %1").arg(query->object().idString()));
+void ClientProxy::onResult(quint32 queryId, const QVariant &value, const QByteArray &type)
+{
+ if (type == "FETCH_OBJECT_R") {
+ log(LogReceive, QString("FETCH_OBJECT_R %1").arg(
+ qvariant_cast<QmlDebugObjectReference>(value).idString()));
+ } else {
+ log(LogReceive, QLatin1String(type));
+ }
- m_rootObjects.append(query->object());
+ if (m_objectTreeQueryIds.contains(queryId))
+ objectTreeFetched(queryId, value);
+ else if (queryId == m_engineQueryId)
+ updateEngineList(value);
+ else if (queryId == m_contextQueryId)
+ contextChanged(value);
+ else if (m_fetchCurrentObjectsQueryIds.contains(queryId))
+ onCurrentObjectsFetched(queryId, value);
+ else
+ emit result(queryId, value);
+}
- int removed = m_objectTreeQuery.removeAll(query);
- Q_ASSERT(removed == 1);
- Q_UNUSED(removed);
- delete query;
+void ClientProxy::objectTreeFetched(quint32 queryId, const QVariant &result)
+{
+ QmlDebugObjectReference obj = qvariant_cast<QmlDebugObjectReference>(result);
+ m_rootObjects.append(obj);
- if (m_objectTreeQuery.isEmpty()) {
+ m_objectTreeQueryIds.removeOne(queryId);
+ if (m_objectTreeQueryIds.isEmpty()) {
int old_count = m_debugIdHash.count();
m_debugIdHash.clear();
m_debugIdHash.reserve(old_count + 1);
- foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects)
+ foreach (const QmlDebugObjectReference &it, m_rootObjects)
buildDebugIdHashRecursive(it);
emit objectTreeUpdated();
@@ -530,14 +584,15 @@ void ClientProxy::objectTreeFetched(QmlJsDebugClient::QDeclarativeDebugQuery::St
}
}
-void ClientProxy::buildDebugIdHashRecursive(const QDeclarativeDebugObjectReference& ref)
+void ClientProxy::buildDebugIdHashRecursive(const QmlDebugObjectReference& ref)
{
QUrl fileUrl = ref.source().url();
int lineNum = ref.source().lineNumber();
int colNum = ref.source().columnNumber();
int rev = 0;
- // handle the case where the url contains the revision number encoded. (for object created by the debugger)
+ // handle the case where the url contains the revision number encoded.
+ //(for object created by the debugger)
static QRegExp rx("(.*)_(\\d+):(\\d+)$");
if (rx.exactMatch(fileUrl.path())) {
fileUrl.setPath(rx.cap(1));
@@ -548,9 +603,10 @@ void ClientProxy::buildDebugIdHashRecursive(const QDeclarativeDebugObjectReferen
const QString filePath = InspectorUi::instance()->findFileInProject(fileUrl);
// append the debug ids in the hash
- m_debugIdHash[qMakePair<QString, int>(filePath, rev)][qMakePair<int, int>(lineNum, colNum)].append(ref.debugId());
+ m_debugIdHash[qMakePair<QString, int>(filePath, rev)][qMakePair<int, int>(
+ lineNum, colNum)].append(ref.debugId());
- foreach (const QDeclarativeDebugObjectReference &it, ref.children())
+ foreach (const QmlDebugObjectReference &it, ref.children())
buildDebugIdHashRecursive(it);
}
@@ -579,12 +635,6 @@ void ClientProxy::setAnimationPaused(bool paused)
m_inspectorClient->setAnimationPaused(paused);
}
-void ClientProxy::changeToColorPickerTool()
-{
- if (isConnected())
- m_inspectorClient->changeToColorPickerTool();
-}
-
void ClientProxy::changeToZoomTool()
{
if (isConnected())
@@ -609,10 +659,12 @@ void ClientProxy::showAppOnTop(bool showOnTop)
}
void ClientProxy::createQmlObject(const QString &qmlText, int parentDebugId,
- const QStringList &imports, const QString &filename, int order)
+ const QStringList &imports,
+ const QString &filename, int order)
{
if (isConnected())
- m_inspectorClient->createQmlObject(qmlText, parentDebugId, imports, filename, order);
+ m_inspectorClient->createQmlObject(qmlText, parentDebugId, imports,
+ filename, order);
}
void ClientProxy::destroyQmlObject(int debugId)
@@ -629,8 +681,10 @@ void ClientProxy::reparentQmlObject(int debugId, int newParent)
void ClientProxy::updateConnected()
{
- bool isConnected = m_inspectorClient && m_inspectorClient->status() == QDeclarativeDebugClient::Enabled
- && m_engineClient && m_engineClient->status() == QDeclarativeDebugClient::Enabled;
+ bool isConnected = m_inspectorClient &&
+ m_inspectorClient->status() == QDeclarativeDebugClient::Enabled &&
+ m_engineClient &&
+ m_engineClient->status() == QDeclarativeDebugClient::Enabled;
if (isConnected != m_isConnected) {
m_isConnected = isConnected;
@@ -645,11 +699,6 @@ void ClientProxy::updateConnected()
void ClientProxy::reloadEngines()
{
- if (m_engineQuery) {
- emit connectionStatusMessage("[Inspector] Waiting for response to previous engine query");
- return;
- }
-
if (!isConnected())
return;
@@ -657,28 +706,18 @@ void ClientProxy::reloadEngines()
log(LogSend, QString("LIST_ENGINES"));
- m_engineQuery = m_engineClient->queryAvailableEngines(m_engineClient);
- if (!m_engineQuery->isWaiting()) {
- updateEngineList();
- } else {
- connect(m_engineQuery, SIGNAL(stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State)),
- this, SLOT(updateEngineList()));
- }
+ m_engineQueryId = m_engineClient->queryAvailableEngines();
}
-QList<QDeclarativeDebugEngineReference> ClientProxy::engines() const
+QList<QmlDebugEngineReference> ClientProxy::engines() const
{
return m_engines;
}
-void ClientProxy::updateEngineList()
+void ClientProxy::updateEngineList(const QVariant &value)
{
- log(LogReceive, QString("LIST_ENGINES_R"));
-
- m_engines = m_engineQuery->engines();
- delete m_engineQuery;
- m_engineQuery = 0;
-
+ m_engines = qvariant_cast<QmlDebugEngineReferenceList>(value);
+ m_engineQueryId = 0;
emit enginesChanged();
}
@@ -695,6 +734,5 @@ bool ClientProxy::isConnected() const
void ClientProxy::newObjects()
{
log(LogReceive, QString("OBJECT_CREATED"));
- if (!m_requestObjectsTimer.isActive())
- m_requestObjectsTimer.start();
+ refreshObjectTree();
}
diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.h b/src/plugins/qmljsinspector/qmljsclientproxy.h
index 2824e3cc2d..bcdef3537b 100644
--- a/src/plugins/qmljsinspector/qmljsclientproxy.h
+++ b/src/plugins/qmljsinspector/qmljsclientproxy.h
@@ -60,39 +60,45 @@ public:
explicit ClientProxy(Debugger::QmlAdapter *adapter, QObject *parent = 0);
~ClientProxy();
- bool setBindingForObject(int objectDebugId,
+ quint32 setBindingForObject(int objectDebugId,
const QString &propertyName,
const QVariant &value,
bool isLiteralValue,
QString source,
int line);
- bool setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody);
- bool resetBindingForObject(int objectDebugId, const QString &propertyName);
- QDeclarativeDebugExpressionQuery *queryExpressionResult(int objectDebugId, const QString &expr);
+ quint32 setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody);
+ quint32 resetBindingForObject(int objectDebugId, const QString &propertyName);
+ quint32 queryExpressionResult(int objectDebugId, const QString &expr);
void clearComponentCache();
bool addObjectWatch(int objectDebugId);
+ bool isObjectBeingWatched(int objectDebugId);
bool removeObjectWatch(int objectDebugId);
void removeAllObjectWatches();
// returns the object references
- QList<QDeclarativeDebugObjectReference> objectReferences() const;
- QDeclarativeDebugObjectReference objectReferenceForId(int debugId) const;
- QDeclarativeDebugObjectReference objectReferenceForId(const QString &objectId) const;
- QDeclarativeDebugObjectReference objectReferenceForLocation(const int line, const int column) const;
- QList<QDeclarativeDebugObjectReference> rootObjectReference() const;
+ QList<QmlDebugObjectReference> objectReferences() const;
+ QmlDebugObjectReference objectReferenceForId(int debugId) const;
+ QmlDebugObjectReference objectReferenceForId(const QString &objectId) const;
+ QmlDebugObjectReference objectReferenceForLocation(const int line, const int column) const;
+ QList<QmlDebugObjectReference> rootObjectReference() const;
DebugIdHash debugIdHash() const { return m_debugIdHash; }
bool isConnected() const;
void setSelectedItemsByDebugId(const QList<int> &debugIds);
- void setSelectedItemsByObjectId(const QList<QDeclarativeDebugObjectReference> &objectRefs);
+ void setSelectedItemsByObjectId(const QList<QmlDebugObjectReference> &objectRefs);
- QList<QDeclarativeDebugEngineReference> engines() const;
+ QList<QmlDebugEngineReference> engines() const;
Debugger::QmlAdapter *qmlAdapter() const;
+ quint32 fetchContextObject(const QmlDebugObjectReference& obj);
+ void addObjectToTree(const QmlDebugObjectReference &obj);
+ void fetchContextObjectRecursive(const QmlDebugContextReference &context, bool clear);
+ void insertObjectInTreeIfNeeded(const QmlDebugObjectReference &object);
+
signals:
void objectTreeUpdated();
void connectionStatusMessage(const QString &text);
@@ -100,12 +106,11 @@ signals:
void aboutToReloadEngines();
void enginesChanged();
- void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &selectedItems);
+ void selectedItemsChanged(const QList<QmlDebugObjectReference> &selectedItems);
void connected();
void disconnected();
- void colorPickerActivated();
void selectToolActivated();
void selectMarqueeToolActivated();
void zoomToolActivated();
@@ -114,9 +119,11 @@ signals:
void designModeBehaviorChanged(bool inDesignMode);
void showAppOnTopChanged(bool showAppOnTop);
void serverReloaded();
- void selectedColorChanged(const QColor &color);
void propertyChanged(int debugId, const QByteArray &propertyName, const QVariant &propertyValue);
+ void result(quint32 queryId, const QVariant &result);
+ void rootContext(const QVariant &context);
+
public slots:
void refreshObjectTree();
void queryEngineContext(int id);
@@ -125,7 +132,6 @@ public slots:
void setDesignModeBehavior(bool inDesignMode);
void setAnimationSpeed(qreal slowDownFactor);
void setAnimationPaused(bool paused);
- void changeToColorPickerTool();
void changeToZoomTool();
void changeToSelectTool();
void changeToSelectMarqueeTool();
@@ -140,21 +146,21 @@ private slots:
void clientStatusChanged(QDeclarativeDebugClient::Status status);
void engineClientStatusChanged(QDeclarativeDebugClient::Status status);
- void contextChanged();
-
void onCurrentObjectsChanged(const QList<int> &debugIds, bool requestIfNeeded = true);
- void updateEngineList();
- void objectTreeFetched(QmlJsDebugClient::QDeclarativeDebugQuery::State state = QmlJsDebugClient::QDeclarativeDebugQuery::Completed);
- void fetchContextObjectRecursive(const QmlJsDebugClient::QDeclarativeDebugContextReference& context);
void newObjects();
- void objectWatchTriggered(const QByteArray &propertyName, const QVariant &propertyValue);
+ void objectWatchTriggered(int debugId, const QByteArray &propertyName, const QVariant &propertyValue);
+ void onResult(quint32 queryId, const QVariant &value, const QByteArray &type);
+ void onCurrentObjectsFetched(quint32 queryId, const QVariant &result);
private:
+ void contextChanged(const QVariant &value);
+ void updateEngineList(const QVariant &value);
+ void objectTreeFetched(quint32 queryId, const QVariant &result);
void updateConnected();
void reloadEngines();
- QList<QDeclarativeDebugObjectReference> objectReferences(const QDeclarativeDebugObjectReference &objectRef) const;
- QDeclarativeDebugObjectReference objectReferenceForId(int debugId, const QDeclarativeDebugObjectReference &ref) const;
+ QList<QmlDebugObjectReference> objectReferences(const QmlDebugObjectReference &objectRef) const;
+ QmlDebugObjectReference objectReferenceForId(int debugId, const QmlDebugObjectReference &ref) const;
enum LogDirection {
LogSend,
@@ -164,22 +170,23 @@ private:
private:
- void buildDebugIdHashRecursive(const QDeclarativeDebugObjectReference &ref);
+ void buildDebugIdHashRecursive(const QmlDebugObjectReference &ref);
QWeakPointer<Debugger::QmlAdapter> m_adapter;
- QDeclarativeEngineDebug *m_engineClient;
+ QmlEngineDebugClient *m_engineClient;
QmlJSInspectorClient *m_inspectorClient;
- QDeclarativeDebugEnginesQuery *m_engineQuery;
- QDeclarativeDebugRootContextQuery *m_contextQuery;
- QList<QDeclarativeDebugObjectQuery *> m_objectTreeQuery;
+ quint32 m_engineQueryId;
+ quint32 m_contextQueryId;
+ QList<quint32> m_objectTreeQueryIds;
+ QList<quint32> m_fetchCurrentObjectsQueryIds;
- QList<QDeclarativeDebugObjectReference> m_rootObjects;
- QList<QDeclarativeDebugEngineReference> m_engines;
- QTimer m_requestObjectsTimer;
+ QList<QmlDebugObjectReference> m_rootObjects;
+ QList<QmlDebugObjectReference> m_fetchCurrentObjects;
+ QmlDebugEngineReferenceList m_engines;
DebugIdHash m_debugIdHash;
- QHash<int, QDeclarativeDebugWatch *> m_objectWatches;
+ QList<int> m_objectWatches;
bool m_isConnected;
};
diff --git a/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp b/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp
index d12e1682e2..25f055a161 100644
--- a/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp
+++ b/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp
@@ -52,7 +52,7 @@ void ContextCrumblePath::updateContextPath(const QStringList &path, const QList<
m_isEmpty = path.isEmpty();
if (m_isEmpty) {
- pushElement(tr("[no context]"), -1);
+ pushElement(tr("[no context]"), -2);
} else {
for (int i = 0; i < path.count(); i++)
pushElement(path[i], debugIds[i]);
diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp
index f499111533..9e18a43919 100644
--- a/src/plugins/qmljsinspector/qmljsinspector.cpp
+++ b/src/plugins/qmljsinspector/qmljsinspector.cpp
@@ -45,10 +45,9 @@
#include <qmljs/parser/qmljsast_p.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljseditor/qmljseditor.h>
-#include <debugger/debuggerconstants.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerplugin.h>
-#include <debugger/debuggerengine.h>
+#include <debugger/qml/qmlengine.h>
#include <debugger/debuggerstartparameters.h>
#include <debugger/qml/qmladapter.h>
@@ -86,6 +85,9 @@
#include <extensionsystem/pluginmanager.h>
+#include <aggregation/aggregate.h>
+#include <find/treeviewfind.h>
+
#include <QDebug>
#include <QStringList>
#include <QTimer>
@@ -119,24 +121,6 @@ enum {
ConnectionAttemptSimultaneousInterval = 500
};
-/**
- * A widget that has the base color.
- */
-class StyledBackground : public QWidget
-{
-public:
- explicit StyledBackground(QWidget *parent = 0)
- : QWidget(parent)
- {}
-
-protected:
- void paintEvent(QPaintEvent *e)
- {
- QPainter p(this);
- p.fillRect(e->rect(), Utils::StyleHelper::baseColor());
- }
-};
-
InspectorUi *InspectorUi::m_instance = 0;
QmlJS::ModelManagerInterface *modelManager()
@@ -149,14 +133,13 @@ InspectorUi::InspectorUi(QObject *parent)
, m_listeningToEditorManager(false)
, m_toolBar(0)
, m_crumblePath(0)
- , m_filterExp(0)
, m_propertyInspector(0)
, m_settings(new InspectorSettings(this))
, m_clientProxy(0)
- , m_qmlEngine(0)
, m_debugQuery(0)
, m_selectionCallbackExpected(false)
, m_cursorPositionChangedExternally(false)
+ , m_onCrumblePathClicked(false)
{
m_instance = this;
m_toolBar = new QmlJsInspectorToolBar(this);
@@ -184,23 +167,31 @@ void InspectorUi::restoreSettings()
m_settings->restoreSettings(Core::ICore::settings());
}
-void InspectorUi::setDebuggerEngine(QObject *qmlEngine)
+void InspectorUi::setDebuggerEngine(QObject *engine)
{
- if (m_qmlEngine && !qmlEngine) {
- disconnect(m_qmlEngine, SIGNAL(tooltipRequested(QPoint,TextEditor::ITextEditor*,int)),
- this, SLOT(showDebuggerTooltip(QPoint,TextEditor::ITextEditor*,int)));
- }
+ Debugger::Internal::QmlEngine *qmlEngine =
+ qobject_cast<Debugger::Internal::QmlEngine *>(engine);
+ QTC_ASSERT(qmlEngine, return);
+ Debugger::DebuggerEngine *masterEngine = qmlEngine;
+ if (qmlEngine->isSlaveEngine())
+ masterEngine = qmlEngine->masterEngine();
- m_qmlEngine = qmlEngine;
- if (m_qmlEngine) {
- connect(m_qmlEngine, SIGNAL(tooltipRequested(QPoint,TextEditor::ITextEditor*,int)),
- this, SLOT(showDebuggerTooltip(QPoint,TextEditor::ITextEditor*,int)));
- }
+ connect(qmlEngine, SIGNAL(tooltipRequested(QPoint,TextEditor::ITextEditor*,int)),
+ this, SLOT(showDebuggerTooltip(QPoint,TextEditor::ITextEditor*,int)));
+ connect(masterEngine, SIGNAL(stateChanged(Debugger::DebuggerState)),
+ this, SLOT(onEngineStateChanged(Debugger::DebuggerState)));
}
-QObject *InspectorUi::debuggerEngine() const
+void InspectorUi::onEngineStateChanged(Debugger::DebuggerState state)
{
- return m_qmlEngine;
+ bool enable = state == Debugger::InferiorRunOk;
+ if (enable)
+ m_toolBar->enable();
+ else
+ m_toolBar->disable();
+ m_crumblePath->setEnabled(enable);
+ m_propertyInspector->setContentsValid(enable);
+ m_propertyInspector->reset();
}
void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextEditor *editor,
@@ -224,7 +215,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE
if (!qmlNode)
return;
- QDeclarativeDebugObjectReference ref;
+ QmlDebugObjectReference ref;
if (QmlJS::AST::Node *node
= qmlEditor->semanticInfo().declaringMemberNoProperties(cursorPos)) {
if (QmlJS::AST::UiObjectMember *objMember = node->uiObjectMemberCast()) {
@@ -249,7 +240,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE
if ((qmlNode->kind == QmlJS::AST::Node::Kind_IdentifierExpression) &&
(m_clientProxy->objectReferenceForId(refToLook).debugId() == -1)) {
query = doubleQuote + QString("local: ") + refToLook + doubleQuote;
- foreach (const QDeclarativeDebugPropertyReference &property, ref.properties()) {
+ foreach (const QmlDebugPropertyReference &property, ref.properties()) {
if (property.name() == wordAtCursor
&& !property.valueTypeName().isEmpty()) {
query = doubleQuote + property.name() + QLatin1Char(':')
@@ -263,7 +254,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE
+ QLatin1Char('+') + refToLook;
} else {
// show properties
- foreach (const QDeclarativeDebugPropertyReference &property, ref.properties()) {
+ foreach (const QmlDebugPropertyReference &property, ref.properties()) {
if (property.name() == wordAtCursor && !property.valueTypeName().isEmpty()) {
query = doubleQuote + property.name() + QLatin1Char(':')
+ doubleQuote + QLatin1Char('+') + property.name();
@@ -275,25 +266,39 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE
if (!query.isEmpty()) {
m_debugQuery = m_clientProxy->queryExpressionResult(ref.debugId(), query);
- connect(m_debugQuery, SIGNAL(stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State)),
- this, SLOT(debugQueryUpdated(QmlJsDebugClient::QDeclarativeDebugQuery::State)));
}
}
}
-void InspectorUi::debugQueryUpdated(QmlJsDebugClient::QDeclarativeDebugQuery::State newState)
+void InspectorUi::onResult(quint32 queryId, const QVariant &result)
{
- if (newState != QDeclarativeDebugExpressionQuery::Completed)
+ if (m_showObjectQueryId == queryId) {
+ m_showObjectQueryId = 0;
+ QmlDebugObjectReference obj = qvariant_cast<QmlDebugObjectReference>(result);
+ m_clientProxy->addObjectToTree(obj);
+ if (m_onCrumblePathClicked) {
+ m_onCrumblePathClicked = false;
+ showObject(obj);
+ }
+ return;
+ }
+
+ if (m_updateObjectQueryIds.contains(queryId)) {
+ m_updateObjectQueryIds.removeOne(queryId);
+ QmlDebugObjectReference obj = qvariant_cast<QmlDebugObjectReference>(result);
+ m_clientProxy->addObjectToTree(obj);
+ if (m_updateObjectQueryIds.empty())
+ showObject(obj);
return;
- if (!m_debugQuery)
+ }
+
+ if (m_debugQuery != queryId)
return;
- QString text = m_debugQuery->result().toString();
+ m_debugQuery = 0;
+ QString text = result.toString();
if (!text.isEmpty())
QToolTip::showText(QCursor::pos(), text);
-
- disconnect(m_debugQuery, SIGNAL(stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State)),
- this, SLOT(debugQueryUpdated(QmlJsDebugClient::QDeclarativeDebugQuery::State)));
}
bool InspectorUi::isConnected() const
@@ -303,7 +308,13 @@ bool InspectorUi::isConnected() const
void InspectorUi::connected(ClientProxy *clientProxy)
{
+ if (m_clientProxy)
+ disconnect(m_clientProxy, SIGNAL(result(quint32,QVariant)),
+ this, SLOT(onResult(quint32,QVariant)));
m_clientProxy = clientProxy;
+ if (m_clientProxy)
+ connect(m_clientProxy, SIGNAL(result(quint32,QVariant)),
+ SLOT(onResult(quint32,QVariant)));
QmlJS::Snapshot snapshot = modelManager()->snapshot();
for (QHash<QString, QmlJSLiveTextPreview *>::const_iterator it = m_textPreviews.constBegin();
@@ -337,7 +348,6 @@ void InspectorUi::disconnected()
disconnectSignals();
disable();
- m_qmlEngine = 0;
resetViews();
applyChangesToQmlInspectorHelper(false);
@@ -350,35 +360,51 @@ void InspectorUi::disconnected()
m_clientProxy = 0;
m_propertyInspector->clear();
m_pendingPreviewDocumentNames.clear();
- setDebuggerEngine(0);
+}
+
+void InspectorUi::onRootContext(const QVariant &value)
+{
+ if (m_crumblePath->dataForLastIndex().toInt() < 0) {
+ m_clientProxy->fetchContextObjectRecursive(
+ qvariant_cast<QmlDebugContextReference>(
+ value), true);
+ } else {
+ for (int i = 1; i < m_crumblePath->length(); i++) {
+ m_updateObjectQueryIds << m_clientProxy->fetchContextObject(
+ m_crumblePath->dataForIndex(i).toInt());
+ }
+ }
}
void InspectorUi::objectTreeReady()
{
- // Should only run once, after debugger startup
- if (!m_clientProxy->rootObjectReference().isEmpty()) {
- selectItems(m_clientProxy->rootObjectReference());
- disconnect(m_clientProxy, SIGNAL(objectTreeUpdated()),
- this, SLOT(objectTreeReady()));
+ if (m_crumblePath->dataForLastIndex().toInt() >= 0) {
+ selectItems(QList<QmlDebugObjectReference>() <<
+ m_clientProxy->objectReferenceForId(
+ m_crumblePath->dataForLastIndex().toInt()));
+ } else {
+ showRoot();
}
}
void InspectorUi::updateEngineList()
{
- QList<QDeclarativeDebugEngineReference> engines = m_clientProxy->engines();
+ QList<QmlDebugEngineReference> engines = m_clientProxy->engines();
//#warning update the QML engines combo
if (engines.isEmpty())
qWarning("qmldebugger: no engines found!");
else {
- const QDeclarativeDebugEngineReference engine = engines.first();
+ const QmlDebugEngineReference engine = engines.first();
m_clientProxy->queryEngineContext(engine.debugId());
}
}
-void InspectorUi::changeSelectedItems(const QList<QDeclarativeDebugObjectReference> &objects)
+void InspectorUi::changeSelectedItems(const QList<QmlDebugObjectReference> &objects)
{
+ if (!m_propertyInspector->contentsValid())
+ return;
if (m_selectionCallbackExpected) {
m_selectionCallbackExpected = false;
return;
@@ -386,9 +412,9 @@ void InspectorUi::changeSelectedItems(const QList<QDeclarativeDebugObjectReferen
m_cursorPositionChangedExternally = true;
// QmlJSLiveTextPreview doesn't provide valid references, only correct debugIds. We need to remap them
- QList <QDeclarativeDebugObjectReference> realList;
- foreach (const QDeclarativeDebugObjectReference &obj, objects) {
- QDeclarativeDebugObjectReference clientRef = m_clientProxy->objectReferenceForId(obj.debugId());
+ QList <QmlDebugObjectReference> realList;
+ foreach (const QmlDebugObjectReference &obj, objects) {
+ QmlDebugObjectReference clientRef = m_clientProxy->objectReferenceForId(obj.debugId());
realList << clientRef;
}
@@ -476,8 +502,8 @@ QmlJSLiveTextPreview *InspectorUi::createPreviewForEditor(Core::IEditor *newEdit
} else {
preview = new QmlJSLiveTextPreview(doc, initdoc, m_clientProxy, this);
connect(preview,
- SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)),
- SLOT(changeSelectedItems(QList<QDeclarativeDebugObjectReference>)));
+ SIGNAL(selectedItemsChanged(QList<QmlDebugObjectReference>)),
+ SLOT(changeSelectedItems(QList<QmlDebugObjectReference>)));
connect(preview, SIGNAL(reloadQmlViewerRequested()),
m_clientProxy, SLOT(reloadQmlViewer()));
connect(preview, SIGNAL(disableLivePreviewRequested()), SLOT(disableLivePreview()));
@@ -503,36 +529,39 @@ void InspectorUi::reloadQmlViewer()
m_clientProxy->reloadQmlViewer();
}
-inline QDeclarativeDebugObjectReference findParentRecursive( int goalDebugId,
- const QList< QDeclarativeDebugObjectReference > &objectsToSearch)
+QmlDebugObjectReference InspectorUi::findParentRecursive(
+ int goalDebugId, const QList<QmlDebugObjectReference > &objectsToSearch)
{
if (goalDebugId == -1)
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
- foreach (const QDeclarativeDebugObjectReference &possibleParent, objectsToSearch) {
+ foreach (const QmlDebugObjectReference &possibleParent, objectsToSearch) {
// Am I a root object? No parent
- if ( possibleParent.debugId() == goalDebugId )
- return QDeclarativeDebugObjectReference();
+ if ( possibleParent.debugId() == goalDebugId && possibleParent.parentId() < 0)
+ return QmlDebugObjectReference();
// Is the goal one of my children?
- foreach (const QDeclarativeDebugObjectReference &child, possibleParent.children())
- if ( child.debugId() == goalDebugId )
+ foreach (const QmlDebugObjectReference &child, possibleParent.children())
+ if ( child.debugId() == goalDebugId ) {
+ m_clientProxy->insertObjectInTreeIfNeeded(child);
return possibleParent;
+ }
// no luck? pass this on
- QDeclarativeDebugObjectReference candidate = findParentRecursive(goalDebugId, possibleParent.children());
+ QmlDebugObjectReference candidate = findParentRecursive(
+ goalDebugId, possibleParent.children());
if (candidate.debugId() != -1)
return candidate;
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
-inline QString displayName(const QDeclarativeDebugObjectReference &obj)
+inline QString displayName(const QmlDebugObjectReference &obj)
{
// special! state names
if (obj.className() == "State") {
- foreach (const QDeclarativeDebugPropertyReference &prop, obj.properties()) {
+ foreach (const QmlDebugPropertyReference &prop, obj.properties()) {
if (prop.name() == "name")
return prop.value().toString();
}
@@ -551,44 +580,52 @@ inline QString displayName(const QDeclarativeDebugObjectReference &obj)
return QString("<%1>").arg(objTypeName);
}
-void InspectorUi::selectItems(const QList<QDeclarativeDebugObjectReference> &objectReferences)
+void InspectorUi::selectItems(const QList<QmlDebugObjectReference> &objectReferences)
{
- foreach (const QDeclarativeDebugObjectReference &objref, objectReferences) {
+ foreach (const QmlDebugObjectReference &objref, objectReferences) {
int debugId = objref.debugId();
if (debugId != -1) {
// select only the first valid element of the list
-
- m_clientProxy->removeAllObjectWatches();
- m_clientProxy->addObjectWatch(debugId);
- QList <QDeclarativeDebugObjectReference> selectionList;
- selectionList << objref;
- m_propertyInspector->setCurrentObjects(selectionList);
- populateCrumblePath(objref);
- gotoObjectReferenceDefinition(objref);
- Debugger::QmlAdapter *qmlAdapter = m_clientProxy->qmlAdapter();
- if (qmlAdapter) {
- qmlAdapter->setCurrentSelectedDebugInfo(debugId, displayName(objref));
- }
+ if (!m_clientProxy->isObjectBeingWatched(debugId))
+ m_clientProxy->removeAllObjectWatches();
+ //Check if the object is complete
+ if (objref.needsMoreData())
+ m_showObjectQueryId = m_clientProxy->fetchContextObject(objref);
+ else
+ showObject(objref);
break;
}
}
}
-bool InspectorUi::isRoot(const QDeclarativeDebugObjectReference &obj) const
+void InspectorUi::showObject(const QmlDebugObjectReference &obj)
+{
+ m_clientProxy->addObjectWatch(obj.debugId());
+ QList <QmlDebugObjectReference> selectionList;
+ selectionList << obj;
+ m_propertyInspector->setCurrentObjects(selectionList);
+ populateCrumblePath(obj);
+ gotoObjectReferenceDefinition(obj);
+ Debugger::QmlAdapter *qmlAdapter = m_clientProxy->qmlAdapter();
+ if (qmlAdapter)
+ qmlAdapter->setCurrentSelectedDebugInfo(obj.debugId(), displayName(obj));
+}
+
+bool InspectorUi::isRoot(const QmlDebugObjectReference &obj) const
{
- foreach (const QDeclarativeDebugObjectReference &rootObj, m_clientProxy->rootObjectReference())
- if (obj.debugId() == rootObj.debugId())
+ foreach (const QmlDebugObjectReference &rootObj, m_clientProxy->rootObjectReference())
+ if (obj.debugId() == rootObj.debugId() && obj.parentId() < 0)
return true;
return false;
}
-void InspectorUi::populateCrumblePath(const QDeclarativeDebugObjectReference &objRef)
+void InspectorUi::populateCrumblePath(const QmlDebugObjectReference &objRef)
{
QStringList crumbleStrings;
QList <int> crumbleData;
// first find path by climbing the hierarchy
- QDeclarativeDebugObjectReference ref = objRef;
+ QmlDebugObjectReference ref = objRef;
crumbleData << objRef.debugId();
crumbleStrings << displayName(objRef);
@@ -597,13 +634,16 @@ void InspectorUi::populateCrumblePath(const QDeclarativeDebugObjectReference &ob
crumbleData.push_front( ref.debugId() );
crumbleStrings.push_front( displayName(ref) );
}
+ //Prepend Root
+ crumbleData.push_front(-1);
+ crumbleStrings.push_front(QLatin1String("/"));
m_crumblePath->updateContextPath(crumbleStrings, crumbleData);
crumbleStrings.clear();
crumbleData.clear();
// now append the children
- foreach (const QDeclarativeDebugObjectReference &child, objRef.children()) {
+ foreach (const QmlDebugObjectReference &child, objRef.children()) {
crumbleData.push_back(child.debugId());
crumbleStrings.push_back( displayName(child) );
}
@@ -611,12 +651,39 @@ void InspectorUi::populateCrumblePath(const QDeclarativeDebugObjectReference &ob
m_crumblePath->addChildren(crumbleStrings, crumbleData);
}
+void InspectorUi::showRoot()
+{
+ QStringList crumbleStrings;
+ QList <int> crumbleData;
+
+ crumbleData << -1;
+ crumbleStrings << QLatin1String("/");
+
+ m_crumblePath->updateContextPath(crumbleStrings, crumbleData);
+ crumbleStrings.clear();
+ crumbleData.clear();
+
+ // now append the children
+ foreach (const QmlDebugObjectReference &child, m_clientProxy->rootObjectReference()) {
+ if (child.parentId() != -1)
+ continue;
+ crumbleData.push_back(child.debugId());
+ crumbleStrings.push_back( displayName(child) );
+ }
+
+ m_crumblePath->addChildren(crumbleStrings, crumbleData);
+ m_propertyInspector->clear();
+ Debugger::QmlAdapter *qmlAdapter = m_clientProxy->qmlAdapter();
+ if (qmlAdapter)
+ qmlAdapter->setCurrentSelectedDebugInfo(-1, QLatin1String("/"));
+}
+
void InspectorUi::selectItems(const QList<int> &objectIds)
{
- QList<QDeclarativeDebugObjectReference> objectReferences;
+ QList<QmlDebugObjectReference> objectReferences;
foreach (int objectId, objectIds)
{
- QDeclarativeDebugObjectReference ref = m_clientProxy->objectReferenceForId(objectId);
+ QmlDebugObjectReference ref = m_clientProxy->objectReferenceForId(objectId);
if (ref.debugId() == objectId)
objectReferences.append(ref);
}
@@ -633,20 +700,18 @@ void InspectorUi::changePropertyValue(int debugId,const QString &propertyName, c
void InspectorUi::enable()
{
m_toolBar->enable();
- m_crumblePath->setEnabled(true);
- m_propertyInspector->setEnabled(true);
- m_filterExp->setEnabled(true);
+ m_crumblePath->clear();
+ m_propertyInspector->clear();
}
void InspectorUi::disable()
{
m_toolBar->disable();
- m_crumblePath->setEnabled(false);
- m_propertyInspector->setEnabled(false);
- m_filterExp->setEnabled(false);
+ m_crumblePath->clear();
+ m_propertyInspector->clear();
}
-QDeclarativeDebugObjectReference InspectorUi::objectReferenceForLocation(const QString &fileName, int cursorPosition) const
+QmlDebugObjectReference InspectorUi::objectReferenceForLocation(const QString &fileName, int cursorPosition) const
{
Core::EditorManager *editorManager = Core::EditorManager::instance();
Core::IEditor *editor = editorManager->openEditor(fileName);
@@ -667,17 +732,17 @@ QDeclarativeDebugObjectReference InspectorUi::objectReferenceForLocation(const Q
}
}
}
- return QDeclarativeDebugObjectReference();
+ return QmlDebugObjectReference();
}
-void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectReference &obj)
+void InspectorUi::gotoObjectReferenceDefinition(const QmlDebugObjectReference &obj)
{
if (m_cursorPositionChangedExternally) {
m_cursorPositionChangedExternally = false;
return;
}
- QDeclarativeDebugFileReference source = obj.source();
+ QmlDebugFileReference source = obj.source();
const QString fileName = m_projectFinder.findFile(source.url());
@@ -690,7 +755,7 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
m_selectionCallbackExpected = true;
if (textEditor) {
- QDeclarativeDebugObjectReference ref = objectReferenceForLocation(fileName);
+ QmlDebugObjectReference ref = objectReferenceForLocation(fileName);
if (ref.debugId() != obj.debugId()) {
m_selectionCallbackExpected = true;
editorManager->addCurrentPositionToNavigationHistory();
@@ -700,20 +765,6 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
}
}
-bool InspectorUi::addQuotesForData(const QVariant &value) const
-{
- switch (value.type()) {
- case QVariant::String:
- case QVariant::Color:
- case QVariant::Date:
- return true;
- default:
- break;
- }
-
- return false;
-}
-
void InspectorUi::setupDockWidgets()
{
Debugger::DebuggerMainWindow *mw = Debugger::DebuggerPlugin::mainWindow();
@@ -733,18 +784,13 @@ void InspectorUi::setupDockWidgets()
inspectorWidget->setWindowTitle(tr("QML Inspector"));
inspectorWidget->setObjectName(Debugger::Constants::DOCKWIDGET_QML_INSPECTOR);
- QWidget *pathAndFilterWidget = new StyledBackground;
+ QWidget *pathAndFilterWidget = new Utils::StyledBar();
pathAndFilterWidget->setMaximumHeight(m_crumblePath->height());
- m_filterExp = new Utils::FilterLineEdit;
- m_filterExp->setPlaceholderText(tr("Filter properties"));
- m_filterExp->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
-
QHBoxLayout *pathAndFilterLayout = new QHBoxLayout(pathAndFilterWidget);
pathAndFilterLayout->setMargin(0);
pathAndFilterLayout->setSpacing(0);
pathAndFilterLayout->addWidget(m_crumblePath);
- pathAndFilterLayout->addWidget(m_filterExp);
QVBoxLayout *wlay = new QVBoxLayout(inspectorWidget);
wlay->setMargin(0);
@@ -752,22 +798,33 @@ void InspectorUi::setupDockWidgets()
inspectorWidget->setLayout(wlay);
wlay->addWidget(pathAndFilterWidget);
wlay->addWidget(m_propertyInspector);
+ wlay->addWidget(new Core::FindToolBarPlaceHolder(inspectorWidget));
QDockWidget *dock = mw->createDockWidget(Debugger::QmlLanguage, inspectorWidget);
dock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
dock->setTitleBarWidget(new QWidget(dock));
+
+ mw->addStagedMenuEntries();
+
+ Aggregation::Aggregate *aggregate = new Aggregation::Aggregate();
+ aggregate->add(m_propertyInspector);
+ aggregate->add(new Find::TreeViewFind(m_propertyInspector));
}
void InspectorUi::crumblePathElementClicked(const QVariant &data)
{
bool ok;
const int debugId = data.toInt(&ok);
- if (!ok || debugId == -1)
+ if (!ok || debugId == -2)
return;
+ if (debugId == -1)
+ return showRoot();
+
QList<int> debugIds;
debugIds << debugId;
+ m_onCrumblePathClicked = true;
selectItems(debugIds);
m_clientProxy->setSelectedItemsByDebugId(debugIds);
}
@@ -843,21 +900,21 @@ void InspectorUi::connectSignals()
connect(m_clientProxy, SIGNAL(propertyChanged(int,QByteArray,QVariant)),
m_propertyInspector, SLOT(propertyValueChanged(int,QByteArray,QVariant)));
- connect(m_clientProxy, SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)),
- this, SLOT(selectItems(QList<QDeclarativeDebugObjectReference>)));
+ connect(m_clientProxy, SIGNAL(selectedItemsChanged(QList<QmlDebugObjectReference>)),
+ this, SLOT(selectItems(QList<QmlDebugObjectReference>)));
connect(m_clientProxy, SIGNAL(enginesChanged()),
this, SLOT(updateEngineList()));
connect(m_clientProxy, SIGNAL(serverReloaded()),
this, SLOT(serverReloaded()));
connect(m_clientProxy, SIGNAL(objectTreeUpdated()),
this, SLOT(objectTreeReady()));
+ connect(m_clientProxy, SIGNAL(rootContext(QVariant)),
+ this, SLOT(onRootContext(QVariant)));
connect(m_clientProxy, SIGNAL(connected()),
this, SLOT(enable()));
connect(m_clientProxy, SIGNAL(disconnected()),
this, SLOT(disable()));
- connect(m_clientProxy, SIGNAL(colorPickerActivated()),
- m_toolBar, SLOT(activateColorPicker()));
connect(m_clientProxy, SIGNAL(selectToolActivated()),
m_toolBar, SLOT(activateSelectTool()));
connect(m_clientProxy, SIGNAL(zoomToolActivated()),
@@ -866,8 +923,6 @@ void InspectorUi::connectSignals()
m_toolBar, SLOT(setDesignModeBehavior(bool)));
connect(m_clientProxy, SIGNAL(showAppOnTopChanged(bool)),
m_toolBar, SLOT(setShowAppOnTop(bool)));
- connect(m_clientProxy, SIGNAL(selectedColorChanged(QColor)),
- m_toolBar, SLOT(setSelectedColor(QColor)));
connect(m_clientProxy, SIGNAL(animationSpeedChanged(qreal)),
m_toolBar, SLOT(setAnimationSpeed(qreal)));
connect(m_clientProxy, SIGNAL(animationPausedChanged(bool)),
@@ -884,17 +939,12 @@ void InspectorUi::connectSignals()
m_clientProxy, SLOT(setAnimationSpeed(qreal)));
connect(m_toolBar, SIGNAL(animationPausedChanged(bool)),
m_clientProxy, SLOT(setAnimationPaused(bool)));
- connect(m_toolBar, SIGNAL(colorPickerSelected()),
- m_clientProxy, SLOT(changeToColorPickerTool()));
connect(m_toolBar, SIGNAL(zoomToolSelected()),
m_clientProxy, SLOT(changeToZoomTool()));
connect(m_toolBar, SIGNAL(selectToolSelected()),
m_clientProxy, SLOT(changeToSelectTool()));
connect(m_toolBar, SIGNAL(showAppOnTopSelected(bool)),
m_clientProxy, SLOT(showAppOnTop(bool)));
-
- connect(m_filterExp, SIGNAL(textChanged(QString)),
- m_propertyInspector, SLOT(filterBy(QString)));
}
void InspectorUi::disconnectSignals()
diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h
index 6a9544f55a..37796b7522 100644
--- a/src/plugins/qmljsinspector/qmljsinspector.h
+++ b/src/plugins/qmljsinspector/qmljsinspector.h
@@ -41,11 +41,11 @@
#include <qmljs/qmljsdocument.h>
#include <qmljs/parser/qmljsastfwd_p.h>
+#include <debugger/debuggerconstants.h>
+
#include <QAction>
#include <QObject>
-QT_FORWARD_DECLARE_CLASS(QLineEdit)
-
namespace ProjectExplorer {
class Project;
class Environment;
@@ -102,7 +102,6 @@ public:
void connected(ClientProxy *clientProxy);
void disconnected();
void setDebuggerEngine(QObject *qmlEngine);
- QObject *debuggerEngine() const;
signals:
void statusMessage(const QString &text);
@@ -112,16 +111,18 @@ public slots:
void reloadQmlViewer();
void serverReloaded();
void setApplyChangesToQmlInspector(bool applyChanges);
+ void onResult(quint32 queryId, const QVariant &result);
private slots:
void enable();
void disable();
- void gotoObjectReferenceDefinition(const QDeclarativeDebugObjectReference &obj);
- void selectItems(const QList<QDeclarativeDebugObjectReference> &objectReferences);
+ void gotoObjectReferenceDefinition(const QmlDebugObjectReference &obj);
+ void selectItems(const QList<QmlDebugObjectReference> &objectReferences);
void selectItems(const QList<int> &objectIds);
- void changeSelectedItems(const QList<QDeclarativeDebugObjectReference> &objects);
+ void changeSelectedItems(const QList<QmlDebugObjectReference> &objects);
void changePropertyValue(int debugId,const QString &propertyName, const QString &valueExpression);
void objectTreeReady();
+ void onRootContext(const QVariant &value);
void updateEngineList();
@@ -133,34 +134,38 @@ private slots:
void updatePendingPreviewDocuments(QmlJS::Document::Ptr doc);
void showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
- void debugQueryUpdated(QmlJsDebugClient::QDeclarativeDebugQuery::State);
+ void onEngineStateChanged(Debugger::DebuggerState state);
private:
- bool addQuotesForData(const QVariant &value) const;
+ void showRoot();
void resetViews();
void initializeDocuments();
void applyChangesToQmlInspectorHelper(bool applyChanges);
void setupDockWidgets();
QString filenameForShadowBuildFile(const QString &filename) const;
- void populateCrumblePath(const QDeclarativeDebugObjectReference &objRef);
- bool isRoot(const QDeclarativeDebugObjectReference &obj) const;
- QDeclarativeDebugObjectReference objectReferenceForLocation(const QString &fileName, int cursorPosition=-1) const;
+ void populateCrumblePath(const QmlDebugObjectReference &objRef);
+ bool isRoot(const QmlDebugObjectReference &obj) const;
+ QmlDebugObjectReference objectReferenceForLocation(const QString &fileName, int cursorPosition=-1) const;
void connectSignals();
void disconnectSignals();
+ void showObject(const QmlDebugObjectReference &obj);
+
+ QmlDebugObjectReference findParentRecursive(
+ int goalDebugId, const QList<QmlDebugObjectReference > &objectsToSearch);
private:
bool m_listeningToEditorManager;
QmlJsInspectorToolBar *m_toolBar;
ContextCrumblePath *m_crumblePath;
- QLineEdit *m_filterExp;
QmlJSPropertyInspector *m_propertyInspector;
InspectorSettings *m_settings;
ClientProxy *m_clientProxy;
- QObject *m_qmlEngine;
- QDeclarativeDebugExpressionQuery *m_debugQuery;
+ quint32 m_debugQuery;
+ quint32 m_showObjectQueryId;
+ QList<quint32> m_updateObjectQueryIds;
// Qml/JS integration
QHash<QString, QmlJSLiveTextPreview *> m_textPreviews;
@@ -172,6 +177,7 @@ private:
static InspectorUi *m_instance;
bool m_selectionCallbackExpected;
bool m_cursorPositionChangedExternally;
+ bool m_onCrumblePathClicked;
};
} // Internal
diff --git a/src/plugins/qmljsinspector/qmljsinspector.pro b/src/plugins/qmljsinspector/qmljsinspector.pro
index 5d39cfa5e0..72d6395bff 100644
--- a/src/plugins/qmljsinspector/qmljsinspector.pro
+++ b/src/plugins/qmljsinspector/qmljsinspector.pro
@@ -2,7 +2,12 @@ TEMPLATE = lib
TARGET = QmlJSInspector
INCLUDEPATH += .
DEPENDPATH += .
-QT += declarative network
+QT += network
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
DEFINES += QMLJSINSPECTOR_LIBRARY
@@ -15,7 +20,6 @@ qmljsclientproxy.h \
qmljsinspector.h \
qmljsinspectortoolbar.h \
qmljslivetextpreview.h \
-qmljstoolbarcolorbox.h \
qmljsinspectorclient.h \
qmljscontextcrumblepath.h \
qmljsinspectorsettings.h \
@@ -27,7 +31,6 @@ qmljsclientproxy.cpp \
qmljsinspector.cpp \
qmljsinspectortoolbar.cpp \
qmljslivetextpreview.cpp \
-qmljstoolbarcolorbox.cpp \
qmljsinspectorclient.cpp \
qmljscontextcrumblepath.cpp \
qmljsinspectorsettings.cpp \
diff --git a/src/plugins/qmljsinspector/qmljsinspector.qbs b/src/plugins/qmljsinspector/qmljsinspector.qbs
index f76b02cc95..c2eb41dee2 100644
--- a/src/plugins/qmljsinspector/qmljsinspector.qbs
+++ b/src/plugins/qmljsinspector/qmljsinspector.qbs
@@ -39,7 +39,6 @@ QtcPlugin {
"qmljsinspector.h",
"qmljsinspectortoolbar.h",
"qmljslivetextpreview.h",
- "qmljstoolbarcolorbox.h",
"qmljsinspectorclient.h",
"qmljscontextcrumblepath.h",
"qmljsinspectorsettings.h",
@@ -50,7 +49,6 @@ QtcPlugin {
"qmljsinspector.cpp",
"qmljsinspectortoolbar.cpp",
"qmljslivetextpreview.cpp",
- "qmljstoolbarcolorbox.cpp",
"qmljsinspectorclient.cpp",
"qmljscontextcrumblepath.cpp",
"qmljsinspectorsettings.cpp",
diff --git a/src/plugins/qmljsinspector/qmljsinspector.qrc b/src/plugins/qmljsinspector/qmljsinspector.qrc
index 9c1a710a6e..51f4e888ab 100644
--- a/src/plugins/qmljsinspector/qmljsinspector.qrc
+++ b/src/plugins/qmljsinspector/qmljsinspector.qrc
@@ -1,6 +1,5 @@
<RCC>
<qresource prefix="/qml">
- <file>images/color-picker.png</file>
<file>images/from-qml.png</file>
<file>images/pause.png</file>
<file>images/reload.png</file>
@@ -9,7 +8,6 @@
<file>images/to-qml.png</file>
<file>images/select-marquee.png</file>
<file>images/zoom.png</file>
- <file>images/color-picker-small.png</file>
<file>images/select-small.png</file>
<file>images/play-small.png</file>
<file>images/to-qml-small.png</file>
@@ -17,7 +15,6 @@
<file>images/from-qml-small.png</file>
<file>images/zoom-small.png</file>
<file>images/select-marquee-small.png</file>
- <file>images/color-picker-small-hicontrast.png</file>
<file>images/app-on-top.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmljsinspector/qmljsinspectorclient.cpp b/src/plugins/qmljsinspector/qmljsinspectorclient.cpp
index d4bf615c8c..0b4bc2aaab 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorclient.cpp
+++ b/src/plugins/qmljsinspector/qmljsinspectorclient.cpp
@@ -33,8 +33,6 @@
#include "qmljsclientproxy.h"
#include "qmljsinspectorconstants.h"
-#include <QColor>
-
using namespace QmlJSDebugger;
namespace QmlJSInspector {
@@ -85,9 +83,7 @@ void QmlJSInspectorClient::messageReceived(const QByteArray &message)
log(LogReceive, type, QString::number(toolId));
- if (toolId == Constants::ColorPickerMode) {
- emit colorPickerActivated();
- } else if (toolId == Constants::ZoomMode) {
+ if (toolId == Constants::ZoomMode) {
emit zoomToolActivated();
} else if (toolId == Constants::SelectionToolMode) {
emit selectToolActivated();
@@ -137,17 +133,8 @@ void QmlJSInspectorClient::messageReceived(const QByteArray &message)
emit reloaded();
break;
}
- case InspectorProtocol::ColorChanged: {
- QColor col;
- ds >> col;
-
- log(LogReceive, type, col.name());
-
- emit selectedColorChanged(col);
- break;
- }
default:
- qWarning() << "Warning: Not handling message:" << type;
+ log(LogReceive, type, QLatin1String("Warning: Not handling message"));
}
}
@@ -182,15 +169,15 @@ void QmlJSInspectorClient::setCurrentObjects(const QList<int> &debugIds)
sendMessage(message);
}
-void recurseObjectIdList(const QDeclarativeDebugObjectReference &ref, QList<int> &debugIds, QList<QString> &objectIds)
+void recurseObjectIdList(const QmlDebugObjectReference &ref, QList<int> &debugIds, QList<QString> &objectIds)
{
debugIds << ref.debugId();
objectIds << ref.idString();
- foreach (const QDeclarativeDebugObjectReference &child, ref.children())
+ foreach (const QmlDebugObjectReference &child, ref.children())
recurseObjectIdList(child, debugIds, objectIds);
}
-void QmlJSInspectorClient::setObjectIdList(const QList<QDeclarativeDebugObjectReference> &objectRoots)
+void QmlJSInspectorClient::setObjectIdList(const QList<QmlDebugObjectReference> &objectRoots)
{
QByteArray message;
QDataStream ds(&message, QIODevice::WriteOnly);
@@ -198,7 +185,7 @@ void QmlJSInspectorClient::setObjectIdList(const QList<QDeclarativeDebugObjectRe
QList<int> debugIds;
QList<QString> objectIds;
- foreach (const QDeclarativeDebugObjectReference &ref, objectRoots)
+ foreach (const QmlDebugObjectReference &ref, objectRoots)
recurseObjectIdList(ref, debugIds, objectIds);
InspectorProtocol::Message cmd = InspectorProtocol::ObjectIdList;
@@ -300,24 +287,6 @@ void QmlJSInspectorClient::setAnimationPaused(bool paused)
sendMessage(message);
}
-void QmlJSInspectorClient::changeToColorPickerTool()
-{
- if (!m_connection || !m_connection->isConnected())
- return;
-
- QByteArray message;
- QDataStream ds(&message, QIODevice::WriteOnly);
-
- InspectorProtocol::Message cmd = InspectorProtocol::ChangeTool;
- InspectorProtocol::Tool tool = InspectorProtocol::ColorPickerTool;
- ds << cmd
- << tool;
-
- log(LogSend, cmd, InspectorProtocol::toString(tool));
-
- sendMessage(message);
-}
-
void QmlJSInspectorClient::changeToSelectTool()
{
if (!m_connection || !m_connection->isConnected())
diff --git a/src/plugins/qmljsinspector/qmljsinspectorclient.h b/src/plugins/qmljsinspector/qmljsinspectorclient.h
index f090841fbd..37420d192c 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorclient.h
+++ b/src/plugins/qmljsinspector/qmljsinspectorclient.h
@@ -51,7 +51,6 @@ public:
void setDesignModeBehavior(bool inDesignMode);
void setAnimationSpeed(qreal slowDownFactor);
void setAnimationPaused(bool paused);
- void changeToColorPickerTool();
void changeToSelectTool();
void changeToSelectMarqueeTool();
void changeToZoomTool();
@@ -68,7 +67,7 @@ public:
QList<int> currentObjects() const;
// ### Qt 4.8: remove if we can have access to qdeclarativecontextdata or id's
- void setObjectIdList(const QList<QDeclarativeDebugObjectReference> &objectRoots);
+ void setObjectIdList(const QList<QmlDebugObjectReference> &objectRoots);
void clearComponentCache();
@@ -76,8 +75,6 @@ signals:
void connectedStatusChanged(QDeclarativeDebugClient::Status status);
void currentObjectsChanged(const QList<int> &debugIds);
- void selectedColorChanged(const QColor &color);
- void colorPickerActivated();
void selectToolActivated();
void selectMarqueeToolActivated();
void zoomToolActivated();
diff --git a/src/plugins/qmljsinspector/qmljsinspectorconstants.h b/src/plugins/qmljsinspector/qmljsinspectorconstants.h
index d338a9eea5..41b985f6a6 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorconstants.h
+++ b/src/plugins/qmljsinspector/qmljsinspectorconstants.h
@@ -42,7 +42,6 @@ const char INFO_OUT_OF_SYNC[] = "QmlInspector.OutOfSyncWarning";
const char PLAY_ACTION[] = "QmlInspector.Play";
const char SELECT_ACTION[] = "QmlInspector.Select";
const char ZOOM_ACTION[] = "QmlInspector.Zoom";
-const char COLOR_PICKER_ACTION[] = "QmlInspector.ColorPicker";
const char FROM_QML_ACTION[] = "QmlInspector.FromQml";
const char SHOW_APP_ON_TOP_ACTION[] = "QmlInspector.ShowAppOnTop";
@@ -56,7 +55,6 @@ enum DesignTool {
MarqueeSelectionToolMode = 2,
MoveToolMode = 3,
ResizeToolMode = 4,
- ColorPickerMode = 5,
ZoomMode = 6
};
diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.h b/src/plugins/qmljsinspector/qmljsinspectorplugin.h
index 34bedf6d65..d3322121fd 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorplugin.h
+++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.h
@@ -52,6 +52,7 @@ class InspectorUi;
class InspectorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlJSInspector.json")
public:
InspectorPlugin();
diff --git a/src/plugins/qmljsinspector/qmljsinspectortoolbar.cpp b/src/plugins/qmljsinspector/qmljsinspectortoolbar.cpp
index 88a015adf2..0c055ba7d3 100644
--- a/src/plugins/qmljsinspector/qmljsinspectortoolbar.cpp
+++ b/src/plugins/qmljsinspector/qmljsinspectortoolbar.cpp
@@ -33,7 +33,6 @@
#include "qmljsinspectortoolbar.h"
#include "qmljsinspectorconstants.h"
-#include "qmljstoolbarcolorbox.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -46,6 +45,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/styledbar.h>
+#include <utils/savedaction.h>
#include <QAction>
#include <QActionGroup>
@@ -70,12 +70,10 @@ QmlJsInspectorToolBar::QmlJsInspectorToolBar(QObject *parent) :
m_playAction(0),
m_selectAction(0),
m_zoomAction(0),
- m_colorPickerAction(0),
m_showAppOnTopAction(0),
m_playSpeedMenuActions(0),
m_playIcon(QIcon(QLatin1String(":/qml/images/play-small.png"))),
m_pauseIcon(QIcon(QLatin1String(":/qml/images/pause-small.png"))),
- m_colorBox(0),
m_emitSignals(true),
m_paused(false),
m_animationSpeed(1.0f),
@@ -92,15 +90,12 @@ void QmlJsInspectorToolBar::setEnabled(bool value)
m_playAction->setEnabled(value);
m_selectAction->setEnabled(value);
m_zoomAction->setEnabled(value);
- m_colorPickerAction->setEnabled(value);
- m_colorBox->setEnabled(value);
}
void QmlJsInspectorToolBar::enable()
{
setEnabled(true);
m_emitSignals = false;
- m_showAppOnTopAction->setChecked(false);
setAnimationSpeed(1.0f);
m_designModeActive = false;
updateDesignModeActions(NoTool);
@@ -115,11 +110,6 @@ void QmlJsInspectorToolBar::disable()
setEnabled(false);
}
-void QmlJsInspectorToolBar::activateColorPicker()
-{
- updateDesignModeActions(ColorPickerMode);
-}
-
void QmlJsInspectorToolBar::activateSelectTool()
{
updateDesignModeActions(SelectionToolMode);
@@ -177,12 +167,22 @@ void QmlJsInspectorToolBar::createActions()
Core::Context context(Debugger::Constants::C_QMLDEBUGGER);
Core::ActionManager *am = Core::ICore::actionManager();
- m_fromQmlAction =
- new QAction(QIcon(QLatin1String(":/qml/images/from-qml-small.png")),
- tr("Apply Changes on Save"), this);
- m_showAppOnTopAction =
- new QAction(QIcon(QLatin1String(":/qml/images/app-on-top.png")),
- tr("Show application on top"), this);
+ m_fromQmlAction = new Utils::SavedAction(this);
+ m_fromQmlAction->setDefaultValue(false);
+ m_fromQmlAction->setSettingsKey(QLatin1String(Constants::S_QML_INSPECTOR),
+ QLatin1String(Constants::FROM_QML_ACTION));
+ m_fromQmlAction->setText(tr("Apply Changes on Save"));
+ m_fromQmlAction->setCheckable(true);
+ m_fromQmlAction->setIcon(QIcon(QLatin1String(":/qml/images/from-qml-small.png")));
+
+ m_showAppOnTopAction = new Utils::SavedAction(this);
+ m_showAppOnTopAction->setDefaultValue(false);
+ m_showAppOnTopAction->setSettingsKey(QLatin1String(Constants::S_QML_INSPECTOR),
+ QLatin1String(Constants::SHOW_APP_ON_TOP_ACTION));
+ m_showAppOnTopAction->setText(tr("Show application on top"));
+ m_showAppOnTopAction->setCheckable(true);
+ m_showAppOnTopAction->setIcon(QIcon(QLatin1String(":/qml/images/app-on-top.png")));
+
m_playAction =
new QAction(m_pauseIcon, tr("Play/Pause Animations"), this);
m_selectAction =
@@ -191,29 +191,18 @@ void QmlJsInspectorToolBar::createActions()
m_zoomAction =
new QAction(QIcon(QLatin1String(":/qml/images/zoom-small.png")),
tr("Zoom"), this);
- m_colorPickerAction =
- new QAction(QIcon(QLatin1String(":/qml/images/color-picker-small.png")),
- tr("Color Picker"), this);
- m_fromQmlAction->setCheckable(true);
- m_fromQmlAction->setChecked(true);
- m_showAppOnTopAction->setCheckable(true);
- m_showAppOnTopAction->setChecked(false);
m_selectAction->setCheckable(true);
m_zoomAction->setCheckable(true);
- m_colorPickerAction->setCheckable(true);
Core::Command *command = am->registerAction(m_playAction, Constants::PLAY_ACTION, context);
command->setAttribute(Core::Command::CA_UpdateIcon);
am->registerAction(m_selectAction, Constants::SELECT_ACTION, context);
am->registerAction(m_zoomAction, Constants::ZOOM_ACTION, context);
- am->registerAction(m_colorPickerAction, Constants::COLOR_PICKER_ACTION, context);
am->registerAction(m_fromQmlAction, Constants::FROM_QML_ACTION, context);
am->registerAction(m_showAppOnTopAction, Constants::SHOW_APP_ON_TOP_ACTION, context);
- m_barWidget = new Utils::StyledBar;
- m_barWidget->setSingleRow(true);
- m_barWidget->setProperty("topBorder", true);
+ m_barWidget = new QWidget;
QMenu *playSpeedMenu = new QMenu(m_barWidget);
m_playSpeedMenuActions = new QActionGroup(this);
@@ -248,19 +237,7 @@ void QmlJsInspectorToolBar::createActions()
toolBarLayout->setMargin(0);
toolBarLayout->setSpacing(5);
- m_operateByInstructionButton = toolButton(am->command(Debugger::Constants::OPERATE_BY_INSTRUCTION)->action());
-
- // Add generic debugging controls
- toolBarLayout->addWidget(toolButton(Debugger::DebuggerPlugin::visibleDebugAction()));
- toolBarLayout->addWidget(toolButton(am->command(Debugger::Constants::STOP)->action()));
- toolBarLayout->addWidget(toolButton(am->command(Debugger::Constants::NEXT)->action()));
- toolBarLayout->addWidget(toolButton(am->command(Debugger::Constants::STEP)->action()));
- toolBarLayout->addWidget(toolButton(am->command(Debugger::Constants::STEPOUT)->action()));
- toolBarLayout->addWidget(m_operateByInstructionButton);
- toolBarLayout->addStretch(1);
-
// QML Helpers
- toolBarLayout->addWidget(new Utils::StyledSeparator);
toolBarLayout->addWidget(toolButton(am->command(Constants::FROM_QML_ACTION)->action()));
toolBarLayout->addWidget(toolButton(am->command(Constants::SHOW_APP_ON_TOP_ACTION)->action()));
m_playButton = toolButton(am->command(Constants::PLAY_ACTION)->action());
@@ -271,26 +248,31 @@ void QmlJsInspectorToolBar::createActions()
toolBarLayout->addWidget(new Utils::StyledSeparator);
toolBarLayout->addWidget(toolButton(am->command(Constants::SELECT_ACTION)->action()));
toolBarLayout->addWidget(toolButton(am->command(Constants::ZOOM_ACTION)->action()));
- toolBarLayout->addWidget(toolButton(am->command(Constants::COLOR_PICKER_ACTION)->action()));
-
- m_colorBox = new ToolBarColorBox(m_barWidget);
- m_colorBox->setMinimumSize(20, 20);
- m_colorBox->setMaximumSize(20, 20);
- m_colorBox->setInnerBorderColor(QColor(192, 192, 192));
- m_colorBox->setOuterBorderColor(QColor(58, 58, 58));
- toolBarLayout->addWidget(m_colorBox);
+ toolBarLayout->addWidget(new Utils::StyledSeparator);
connect(m_fromQmlAction, SIGNAL(triggered()), SLOT(activateFromQml()));
connect(m_showAppOnTopAction, SIGNAL(triggered()), SLOT(showAppOnTopClick()));
connect(m_playAction, SIGNAL(triggered()), SLOT(activatePlayOnClick()));
- connect(m_colorPickerAction, SIGNAL(triggered(bool)), SLOT(colorPickerTriggered(bool)));
connect(m_selectAction, SIGNAL(triggered(bool)), SLOT(selectToolTriggered(bool)));
connect(m_zoomAction, SIGNAL(triggered(bool)), SLOT(zoomToolTriggered(bool)));
- Debugger::DebuggerMainWindow *mw = Debugger::DebuggerPlugin::mainWindow();
- activeDebugLanguagesChanged(mw->activeDebugLanguages());
- connect(mw, SIGNAL(activeDebugLanguagesChanged(Debugger::DebuggerLanguages)),
- this, SLOT(activeDebugLanguagesChanged(Debugger::DebuggerLanguages)));
+ readSettings();
+ connect(Core::ICore::instance(),
+ SIGNAL(saveSettingsRequested()), SLOT(writeSettings()));
+}
+
+void QmlJsInspectorToolBar::readSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ m_fromQmlAction->readSettings(settings);
+ m_showAppOnTopAction->readSettings(settings);
+}
+
+void QmlJsInspectorToolBar::writeSettings() const
+{
+ QSettings *settings = Core::ICore::settings();
+ m_fromQmlAction->writeSettings(settings);
+ m_showAppOnTopAction->writeSettings(settings);
}
QWidget *QmlJsInspectorToolBar::widget() const
@@ -320,19 +302,6 @@ void QmlJsInspectorToolBar::updatePlayAction()
m_playAction->setIcon(m_paused ? m_playIcon : m_pauseIcon);
}
-void QmlJsInspectorToolBar::colorPickerTriggered(bool checked)
-{
- updateDesignModeActions(ColorPickerMode);
-
- if (m_designModeActive != checked) {
- m_designModeActive = checked;
- emit designModeSelected(checked);
- }
-
- if (checked)
- emit colorPickerSelected();
-}
-
void QmlJsInspectorToolBar::selectToolTriggered(bool checked)
{
updateDesignModeActions(SelectionToolMode);
@@ -365,28 +334,17 @@ void QmlJsInspectorToolBar::showAppOnTopClick()
emit showAppOnTopSelected(m_showAppOnTopAction->isChecked());
}
-void QmlJsInspectorToolBar::setSelectedColor(const QColor &color)
-{
- m_colorBox->setColor(color);
-}
-
void QmlJsInspectorToolBar::activateFromQml()
{
if (m_emitSignals)
emit applyChangesFromQmlFileTriggered(m_fromQmlAction->isChecked());
}
-void QmlJsInspectorToolBar::activeDebugLanguagesChanged(Debugger::DebuggerLanguages languages)
-{
- m_operateByInstructionButton->setVisible(languages & Debugger::CppLanguage);
-}
-
void QmlJsInspectorToolBar::updateDesignModeActions(DesignTool activeTool)
{
m_activeTool = activeTool;
m_selectAction->setChecked(m_designModeActive && (m_activeTool == SelectionToolMode));
m_zoomAction->setChecked(m_designModeActive && (m_activeTool == ZoomMode));
- m_colorPickerAction->setChecked(m_designModeActive && (m_activeTool == ColorPickerMode));
}
} // namespace Internal
diff --git a/src/plugins/qmljsinspector/qmljsinspectortoolbar.h b/src/plugins/qmljsinspector/qmljsinspectortoolbar.h
index b533c38de6..99c4fd9a39 100644
--- a/src/plugins/qmljsinspector/qmljsinspectortoolbar.h
+++ b/src/plugins/qmljsinspector/qmljsinspectortoolbar.h
@@ -41,18 +41,16 @@
QT_BEGIN_NAMESPACE
class QAction;
class QActionGroup;
-class QColor;
class QToolButton;
QT_END_NAMESPACE
namespace Utils {
class StyledBar;
+class SavedAction;
}
namespace QmlJSInspector {
-class ToolBarColorBox;
-
namespace Internal {
class QmlJsInspectorToolBar : public QObject
@@ -66,20 +64,20 @@ public:
MarqueeSelectionToolMode = 2,
MoveToolMode = 3,
ResizeToolMode = 4,
- ColorPickerMode = 5,
ZoomMode = 6
};
explicit QmlJsInspectorToolBar(QObject *parent = 0);
void createActions();
QWidget *widget() const;
+ void readSettings();
public slots:
+ void writeSettings() const;
void setEnabled(bool value);
void enable();
void disable();
- void activateColorPicker();
void activateSelectTool();
void activateZoomTool();
@@ -88,14 +86,12 @@ public slots:
void setDesignModeBehavior(bool inDesignMode);
void setShowAppOnTop(bool showAppOnTop);
- void setSelectedColor(const QColor &color);
signals:
void applyChangesFromQmlFileTriggered(bool isChecked);
void designModeSelected(bool);
void reloadSelected();
- void colorPickerSelected();
void selectToolSelected();
void zoomToolSelected();
@@ -106,7 +102,6 @@ signals:
private slots:
void activatePlayOnClick();
- void colorPickerTriggered(bool checked);
void selectToolTriggered(bool checked);
void zoomToolTriggered(bool checked);
@@ -118,20 +113,15 @@ private slots:
void updatePlayAction();
- void activeDebugLanguagesChanged(Debugger::DebuggerLanguages languages);
-
private:
void updateDesignModeActions(DesignTool activeTool);
- QToolButton *m_operateByInstructionButton;
-
- QAction *m_fromQmlAction;
+ Utils::SavedAction *m_fromQmlAction;
QAction *m_playAction;
QAction *m_selectAction;
QAction *m_zoomAction;
- QAction *m_colorPickerAction;
- QAction *m_showAppOnTopAction;
+ Utils::SavedAction *m_showAppOnTopAction;
QActionGroup *m_playSpeedMenuActions;
@@ -139,8 +129,6 @@ private:
QIcon m_playIcon;
QIcon m_pauseIcon;
- ToolBarColorBox *m_colorBox;
-
bool m_emitSignals;
bool m_paused;
qreal m_animationSpeed;
@@ -148,7 +136,7 @@ private:
bool m_designModeActive;
DesignTool m_activeTool;
- Utils::StyledBar *m_barWidget;
+ QWidget *m_barWidget;
};
} // namespace Internal
diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
index 17eed26cde..0496e30b48 100644
--- a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
+++ b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
@@ -178,6 +178,8 @@ QmlJSLiveTextPreview::QmlJSLiveTextPreview(const QmlJS::Document::Ptr &doc,
, m_initialDoc(initDoc)
, m_applyChangesToQmlInspector(true)
, m_clientProxy(clientProxy)
+ , m_nodeForOffset(0)
+ , m_updateNodeForOffset(false)
{
Q_ASSERT(doc->fileName() == initDoc->fileName());
m_filename = doc->fileName();
@@ -200,17 +202,32 @@ void QmlJSLiveTextPreview::resetInitialDoc(const QmlJS::Document::Ptr &doc)
}
-QList<int> QmlJSLiveTextPreview::objectReferencesForOffset(quint32 offset) const
+QList<int> QmlJSLiveTextPreview::objectReferencesForOffset(quint32 offset)
{
QList<int> result;
QHashIterator<QmlJS::AST::UiObjectMember*, QList<int> > iter(m_debugIds);
+ QmlJS::AST::UiObjectMember *possibleNode = 0;
while(iter.hasNext()) {
iter.next();
QmlJS::AST::UiObjectMember *member = iter.key();
- if (member->firstSourceLocation().offset == offset) {
- result = iter.value();
- break;
+ quint32 startOffset = member->firstSourceLocation().offset;
+ quint32 endOffset = member->lastSourceLocation().offset;
+ if (startOffset <= offset && offset <= endOffset) {
+ if (!possibleNode)
+ possibleNode = member;
+ if (possibleNode->firstSourceLocation().offset <= startOffset &&
+ endOffset <= possibleNode->lastSourceLocation().offset)
+ possibleNode = member;
+ }
+ }
+ if (possibleNode) {
+ if (possibleNode != m_nodeForOffset) {
+ //We have found a better match, set flag so that we can
+ //query again to check if this is the best match for the offset
+ m_updateNodeForOffset = true;
+ m_nodeForOffset = possibleNode;
}
+ result = m_debugIds.value(possibleNode);
}
return result;
}
@@ -220,7 +237,9 @@ void QmlJSLiveTextPreview::changeSelectedElements(QList<int> offsets, const QStr
if (m_editors.isEmpty() || !m_previousDoc || !m_clientProxy)
return;
- QDeclarativeDebugObjectReference objectRefUnderCursor;
+ m_updateNodeForOffset = false;
+ m_lastOffsets = offsets;
+ QmlDebugObjectReference objectRefUnderCursor;
objectRefUnderCursor = m_clientProxy.data()->objectReferenceForId(wordAtCursor);
QList<int> selectedReferences;
@@ -252,20 +271,20 @@ void QmlJSLiveTextPreview::changeSelectedElements(QList<int> offsets, const QStr
}
if (!selectedReferences.isEmpty()) {
- QList<QDeclarativeDebugObjectReference> refs;
+ QList<QmlDebugObjectReference> refs;
foreach(int i, selectedReferences)
- refs << QDeclarativeDebugObjectReference(i);
+ refs << QmlDebugObjectReference(i);
emit selectedItemsChanged(refs);
}
}
-static QList<int> findRootObjectRecursive(const QDeclarativeDebugObjectReference &object, const Document::Ptr &doc)
+static QList<int> findRootObjectRecursive(const QmlDebugObjectReference &object, const Document::Ptr &doc)
{
QList<int> result;
if (object.className() == doc->componentName())
result += object.debugId();
- foreach (const QDeclarativeDebugObjectReference &it, object.children()) {
+ foreach (const QmlDebugObjectReference &it, object.children()) {
result += findRootObjectRecursive(it, doc);
}
return result;
@@ -305,7 +324,7 @@ void QmlJSLiveTextPreview::updateDebugIds()
if(doc->qmlProgram()->members && doc->qmlProgram()->members->member) {
UiObjectMember* root = doc->qmlProgram()->members->member;
QList<int> r;
- foreach(const QDeclarativeDebugObjectReference& it, clientProxy->rootObjectReference())
+ foreach (const QmlDebugObjectReference& it, clientProxy->rootObjectReference())
r += findRootObjectRecursive(it, doc);
if (!r.isEmpty())
m_debugIds[root] += r;
@@ -338,6 +357,8 @@ void QmlJSLiveTextPreview::updateDebugIds()
m_debugIds[it2.key()] += it2.value();
}
}
+ if (m_updateNodeForOffset)
+ changeSelectedElements(m_lastOffsets, QString());
}
@@ -464,7 +485,10 @@ protected:
if (isLiteral)
expr = castToLiteral(scriptCode, scriptBinding);
appliedChangesToViewer = true;
- m_clientProxy->setBindingForObject(debugId, propertyName, expr, isLiteral, document()->fileName(), scriptBinding->firstSourceLocation().startLine);
+ m_clientProxy->setBindingForObject(
+ debugId, propertyName, expr,
+ isLiteral, document()->fileName(),
+ scriptBinding->firstSourceLocation().startLine);
}
virtual void resetBindingForObject(int debugId, const QString &propertyName)
diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.h b/src/plugins/qmljsinspector/qmljslivetextpreview.h
index 661b2d0e77..6ada1e56ad 100644
--- a/src/plugins/qmljsinspector/qmljslivetextpreview.h
+++ b/src/plugins/qmljsinspector/qmljslivetextpreview.h
@@ -72,8 +72,8 @@ public:
void associateEditor(Core::IEditor *editor);
void unassociateEditor(Core::IEditor *editor);
- void setActiveObject(const QDeclarativeDebugObjectReference &object);
- void mapObjectToQml(const QDeclarativeDebugObjectReference &object);
+ void setActiveObject(const QmlDebugObjectReference &object);
+ void mapObjectToQml(const QmlDebugObjectReference &object);
void resetInitialDoc(const QmlJS::Document::Ptr &doc);
void setClientProxy(ClientProxy *clientProxy);
@@ -85,7 +85,7 @@ public:
};
signals:
- void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects);
+ void selectedItemsChanged(const QList<QmlDebugObjectReference> &objects);
void reloadQmlViewerRequested();
void disableLivePreviewRequested();
@@ -101,7 +101,7 @@ private slots:
private:
static QmlJS::ModelManagerInterface *modelManager();
- QList<int> objectReferencesForOffset(quint32 offset) const;
+ QList<int> objectReferencesForOffset(quint32 offset);
QVariant castToLiteral(const QString &expression, QmlJS::AST::UiScriptBinding *scriptBinding);
void showSyncWarning(UnsyncronizableChangeType unsyncronizableChangeType, const QString &elementName,
unsigned line, unsigned column);
@@ -120,6 +120,9 @@ private:
bool m_applyChangesToQmlInspector;
QmlJS::Document::Ptr m_docWithUnappliedChanges;
QWeakPointer<ClientProxy> m_clientProxy;
+ QList<int> m_lastOffsets;
+ QmlJS::AST::UiObjectMember *m_nodeForOffset;
+ bool m_updateNodeForOffset;
};
diff --git a/src/plugins/qmljsinspector/qmljsprivateapi.h b/src/plugins/qmljsinspector/qmljsprivateapi.h
index a8654ba926..003938c548 100644
--- a/src/plugins/qmljsinspector/qmljsprivateapi.h
+++ b/src/plugins/qmljsinspector/qmljsprivateapi.h
@@ -32,8 +32,10 @@
#ifndef QMLJSPRIVATEAPI_H
#define QMLJSPRIVATEAPI_H
-#include <qmljsdebugclient/qdeclarativeenginedebug.h>
+#include <qmljsdebugclient/qmlenginedebugclient.h>
#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include <qmljsdebugclient/qdeclarativeengineclient.h>
+#include <qmljsdebugclient/qmldebuggerclient.h>
using namespace QmlJsDebugClient;
diff --git a/src/plugins/qmljsinspector/qmljspropertyinspector.cpp b/src/plugins/qmljsinspector/qmljspropertyinspector.cpp
index 4ee78abf2a..9f4c325c48 100644
--- a/src/plugins/qmljsinspector/qmljspropertyinspector.cpp
+++ b/src/plugins/qmljsinspector/qmljspropertyinspector.cpp
@@ -47,6 +47,10 @@
#include <utils/qtcassert.h>
+const int PROPERTY_NAME_COLUMN = 0;
+const int PROPERTY_TYPE_COLUMN = 1;
+const int PROPERTY_VALUE_COLUMN = 2;
+
namespace QmlJSInspector {
namespace Internal {
@@ -65,16 +69,16 @@ class PropertyEditDelegate : public QItemDelegate
const QModelIndex &index) const
{
Q_UNUSED(option);
- if (index.column() != 1)
+ if (index.column() != PROPERTY_VALUE_COLUMN)
return 0;
switch (m_treeWidget->getTypeFor(index.row())) {
case QmlJSPropertyInspector::BooleanType: {
// invert the bool, skip editor
- int objectId = m_treeWidget->getData(index.row(), 0, Qt::UserRole).toInt();
- QString propertyName = m_treeWidget->getData(index.row(), 0, Qt::DisplayRole).toString();
- bool propertyValue = m_treeWidget->getData(index.row(), 1, Qt::DisplayRole).toBool();
+ int objectId = m_treeWidget->getData(index.row(), PROPERTY_NAME_COLUMN, Qt::UserRole).toInt();
+ QString propertyName = m_treeWidget->getData(index.row(), PROPERTY_NAME_COLUMN, Qt::DisplayRole).toString();
+ bool propertyValue = m_treeWidget->getData(index.row(), PROPERTY_VALUE_COLUMN, Qt::DisplayRole).toBool();
m_treeWidget->propertyValueEdited(objectId, propertyName, !propertyValue?"true":"false");
return 0;
}
@@ -95,7 +99,7 @@ class PropertyEditDelegate : public QItemDelegate
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
- QVariant data = m_treeWidget->getData(index.row(), 1, Qt::DisplayRole);
+ QVariant data = m_treeWidget->getData(index.row(), PROPERTY_VALUE_COLUMN, Qt::DisplayRole);
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(data.toString());
}
@@ -104,11 +108,11 @@ class PropertyEditDelegate : public QItemDelegate
{
Q_UNUSED(model);
- int objectId = m_treeWidget->getData(index.row(), 0, Qt::UserRole).toInt();
+ int objectId = m_treeWidget->getData(index.row(), PROPERTY_NAME_COLUMN, Qt::UserRole).toInt();
if (objectId == -1)
return;
- QString propertyName = m_treeWidget->getData(index.row(), 0, Qt::DisplayRole).toString();
+ QString propertyName = m_treeWidget->getData(index.row(), PROPERTY_NAME_COLUMN, Qt::DisplayRole).toString();
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
QString propertyValue = lineEdit->text();
@@ -240,20 +244,6 @@ void ColorChooserDialog::acceptColor(const QColor &color)
}
// *************************************************************************
-// FILTER
-// *************************************************************************
-bool PropertiesFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
- QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
- QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
-
- return (sourceModel()->data(index0).toString().contains(filterRegExp())
- || sourceModel()->data(index1).toString().contains(filterRegExp())
- || sourceModel()->data(index2).toString().contains(filterRegExp()));
-}
-
-// *************************************************************************
// QmlJSObjectTree
// *************************************************************************
inline QString cleanPropertyValue(QString propertyValue)
@@ -265,44 +255,76 @@ inline QString cleanPropertyValue(QString propertyValue)
return propertyValue;
}
-QmlJSPropertyInspector::QmlJSPropertyInspector(QWidget *parent)
- : QTreeView(parent)
+// *************************************************************************
+// QmlJSPropertyInspectorModel
+// *************************************************************************
+QmlJSPropertyInspectorModel::QmlJSPropertyInspectorModel()
+ : QStandardItemModel()
+ , m_contentsValid(false)
{
- setAttribute(Qt::WA_MacShowFocusRect, false);
- setFrameStyle(QFrame::NoFrame);
- setExpandsOnDoubleClick(true);
+}
+
+Qt::ItemFlags QmlJSPropertyInspectorModel::flags(const QModelIndex &index) const
+{
+ return m_contentsValid ? QStandardItemModel::flags(index) : Qt::ItemFlags();
+}
- header()->setResizeMode(QHeaderView::ResizeToContents);
- header()->setMinimumSectionSize(150);
- setRootIsDecorated(false);
+QVariant QmlJSPropertyInspectorModel::headerData(int section, Qt::Orientation orient, int role) const
+{
+ if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case PROPERTY_NAME_COLUMN: return tr("Name");
+ case PROPERTY_VALUE_COLUMN: return tr("Value");
+ case PROPERTY_TYPE_COLUMN: return tr("Type");
+ };
+ }
+ return QStandardItemModel::headerData(section, orient, role);
+}
- setItemDelegateForColumn(1, new PropertyEditDelegate(this));
+void QmlJSPropertyInspectorModel::setContentsValid(bool contentsValid)
+{
+ m_contentsValid = contentsValid;
+}
- m_filter = new PropertiesFilter(this);
- m_filter->setSourceModel(&m_model);
- setModel(m_filter);
+bool QmlJSPropertyInspectorModel::contentsValid() const
+{
+ return m_contentsValid;
}
-void QmlJSPropertyInspector::filterBy(const QString &expression)
+QmlJSPropertyInspector::QmlJSPropertyInspector(QWidget *parent)
+ : Utils::BaseTreeView(parent)
{
- m_filter->setFilterWildcard(expression);
- m_filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ setItemDelegateForColumn(PROPERTY_VALUE_COLUMN, new PropertyEditDelegate(this));
+
+ setModel(&m_model);
+ //Add an empty Row to make the headers visible!
+ addRow(QString(), QString(), QString(), -1, false);
}
void QmlJSPropertyInspector::clear()
{
- m_model.clear();
+ m_model.removeRows(0, m_model.rowCount());
m_currentObjects.clear();
}
-void QmlJSPropertyInspector::setCurrentObjects(const QList<QDeclarativeDebugObjectReference> &objectList)
+void QmlJSPropertyInspector::setContentsValid(bool contentsValid)
+{
+ m_model.setContentsValid(contentsValid);
+}
+
+bool QmlJSPropertyInspector::contentsValid() const
+{
+ return m_model.contentsValid();
+}
+
+void QmlJSPropertyInspector::setCurrentObjects(const QList<QmlDebugObjectReference> &objectList)
{
if (objectList.isEmpty())
return;
clear();
- foreach (const QDeclarativeDebugObjectReference &obj, objectList) {
+ foreach (const QmlDebugObjectReference &obj, objectList) {
m_currentObjects << obj.debugId();
buildPropertyTree(obj);
}
@@ -310,12 +332,12 @@ void QmlJSPropertyInspector::setCurrentObjects(const QList<QDeclarativeDebugObje
QVariant QmlJSPropertyInspector::getData(int row, int column, int role) const
{
- return m_filter->data(m_filter->index(row, column), role);
+ return m_model.data(m_model.index(row, column), role);
}
QmlJSPropertyInspector::PropertyType QmlJSPropertyInspector::getTypeFor(int row) const
{
- return static_cast<QmlJSPropertyInspector::PropertyType>(m_filter->data(m_filter->index(row,2),Qt::UserRole).toInt());
+ return static_cast<QmlJSPropertyInspector::PropertyType>(m_model.data(m_model.index(row, PROPERTY_TYPE_COLUMN), Qt::UserRole).toInt());
}
void QmlJSPropertyInspector::propertyValueChanged(int debugId, const QByteArray &propertyName, const QVariant &propertyValue)
@@ -325,18 +347,18 @@ void QmlJSPropertyInspector::propertyValueChanged(int debugId, const QByteArray
QString propertyNameS = QString(propertyName);
for (int i = 0; i < m_model.rowCount(); i++) {
- if (m_model.data(m_model.index(i, 0), Qt::DisplayRole).toString() == propertyNameS &&
- m_model.data(m_model.index(i, 0), Qt::UserRole).toInt() == debugId) {
- QString oldData = m_model.data(m_model.index(i, 1), Qt::DisplayRole).toString();
+ if (m_model.data(m_model.index(i, PROPERTY_NAME_COLUMN), Qt::DisplayRole).toString() == propertyNameS &&
+ m_model.data(m_model.index(i, PROPERTY_NAME_COLUMN), Qt::UserRole).toInt() == debugId) {
+ QString oldData = m_model.data(m_model.index(i, PROPERTY_VALUE_COLUMN), Qt::DisplayRole).toString();
QString newData = propertyValue.toString();
if (QString(propertyValue.typeName()) == "QColor")
newData = extendedNameFromColor(propertyValue);
if (oldData != newData) {
- m_model.setData(m_model.index(i, 1), newData, Qt::DisplayRole);
- m_model.item(i, 1)->setToolTip(newData);
- m_model.item(i, 0)->setForeground(QBrush(Qt::red));
- m_model.item(i, 1)->setForeground(QBrush(Qt::red));
- m_model.item(i, 2)->setForeground(QBrush(Qt::red));
+ m_model.setData(m_model.index(i, PROPERTY_VALUE_COLUMN), newData, Qt::DisplayRole);
+ m_model.item(i, PROPERTY_VALUE_COLUMN)->setToolTip(newData);
+ m_model.item(i, PROPERTY_NAME_COLUMN)->setForeground(QBrush(Qt::red));
+ m_model.item(i, PROPERTY_VALUE_COLUMN)->setForeground(QBrush(Qt::red));
+ m_model.item(i, PROPERTY_TYPE_COLUMN)->setForeground(QBrush(Qt::red));
if (getTypeFor(i) == QmlJSPropertyInspector::ColorType)
setColorIcon(i);
}
@@ -350,7 +372,7 @@ void QmlJSPropertyInspector::propertyValueEdited(const int objectId,const QStrin
emit changePropertyValue(objectId, propertyName, propertyValue);
}
-void QmlJSPropertyInspector::buildPropertyTree(const QDeclarativeDebugObjectReference &obj)
+void QmlJSPropertyInspector::buildPropertyTree(const QmlDebugObjectReference &obj)
{
// Strip off the misleading metadata
QString objTypeName = obj.className();
@@ -375,7 +397,7 @@ void QmlJSPropertyInspector::buildPropertyTree(const QDeclarativeDebugObjectRefe
false);
}
- foreach (const QDeclarativeDebugPropertyReference &prop, obj.properties()) {
+ foreach (const QmlDebugPropertyReference &prop, obj.properties()) {
QString propertyName = prop.name();
QString propertyValue = prop.value().toString();
@@ -389,9 +411,9 @@ void QmlJSPropertyInspector::buildPropertyTree(const QDeclarativeDebugObjectRefe
addRow(propertyName, propertyValue, prop.valueTypeName(), obj.debugId(), prop.hasNotifySignal());
}
- m_model.setHeaderData(0,Qt::Horizontal,QVariant("name"));
- m_model.setHeaderData(1,Qt::Horizontal,QVariant("value"));
- m_model.setHeaderData(2,Qt::Horizontal,QVariant("type"));
+ m_model.setHeaderData(PROPERTY_NAME_COLUMN, Qt::Horizontal,QVariant("name"));
+ m_model.setHeaderData(PROPERTY_VALUE_COLUMN, Qt::Horizontal,QVariant("value"));
+ m_model.setHeaderData(PROPERTY_TYPE_COLUMN, Qt::Horizontal,QVariant("type"));
}
@@ -426,7 +448,7 @@ void QmlJSPropertyInspector::addRow(const QString &name,const QString &value, co
typeColumn->setData(typeCode, Qt::UserRole);
QList<QStandardItem *> newRow;
- newRow << nameColumn << valueColumn << typeColumn;
+ newRow << nameColumn << typeColumn << valueColumn;
m_model.appendRow(newRow);
if (typeCode == QmlJSPropertyInspector::ColorType)
@@ -435,7 +457,7 @@ void QmlJSPropertyInspector::addRow(const QString &name,const QString &value, co
void QmlJSPropertyInspector::setColorIcon(int row)
{
- QStandardItem *item = m_model.itemFromIndex(m_model.index(row, 1));
+ QStandardItem *item = m_model.item(row, PROPERTY_VALUE_COLUMN);
QColor color = colorFromExtendedName(item->data(Qt::DisplayRole).toString());
int recomendedLength = viewOptions().decorationSize.height() - 2;
@@ -457,7 +479,7 @@ void QmlJSPropertyInspector::contextMenuEvent(QContextMenuEvent *ev)
bool isEditable = false;
bool isColor = false;
if (itemIndex.isValid()) {
- isEditable = m_model.itemFromIndex(m_filter->mapToSource(m_filter->index(itemIndex.row(), 1)))->isEditable();
+ isEditable = m_model.item(itemIndex.row(), PROPERTY_VALUE_COLUMN)->isEditable();
isColor = (getTypeFor(itemIndex.row()) == QmlJSPropertyInspector::ColorType);
}
@@ -468,6 +490,7 @@ void QmlJSPropertyInspector::contextMenuEvent(QContextMenuEvent *ev)
QAction colorAction(tr("Choose color"), this);
if (isColor)
menu.addAction(&colorAction);
+ addBaseContextActions(&menu);
QAction *action = menu.exec(ev->globalPos());
if (action == 0)
@@ -477,13 +500,14 @@ void QmlJSPropertyInspector::contextMenuEvent(QContextMenuEvent *ev)
openExpressionEditor(itemIndex);
if (action == &colorAction)
openColorSelector(itemIndex);
+ handleBaseContextAction(action);
}
void QmlJSPropertyInspector::openExpressionEditor(const QModelIndex &itemIndex)
{
- const QString propertyName = getData(itemIndex.row(), 0, Qt::DisplayRole).toString();
+ const QString propertyName = getData(itemIndex.row(), PROPERTY_NAME_COLUMN, Qt::DisplayRole).toString();
const QString dialogText = tr("JavaScript expression for %1").arg(propertyName);
- const int objectId = getData(itemIndex.row(), 0, Qt::UserRole).toInt();
+ const int objectId = getData(itemIndex.row(), PROPERTY_NAME_COLUMN, Qt::UserRole).toInt();
ExpressionEdit *expressionDialog = new ExpressionEdit(dialogText);
expressionDialog->setItemData(objectId, propertyName);
@@ -496,10 +520,10 @@ void QmlJSPropertyInspector::openExpressionEditor(const QModelIndex &itemIndex)
void QmlJSPropertyInspector::openColorSelector(const QModelIndex &itemIndex)
{
- const QString propertyName = getData(itemIndex.row(), 0, Qt::DisplayRole).toString();
+ const QString propertyName = getData(itemIndex.row(), PROPERTY_NAME_COLUMN, Qt::DisplayRole).toString();
const QString dialogText = tr("Color selection for %1").arg(propertyName);
- const int objectId = getData(itemIndex.row(), 0, Qt::UserRole).toInt();
- const QString propertyValue = getData(itemIndex.row(), 1, Qt::DisplayRole).toString();
+ const int objectId = getData(itemIndex.row(), PROPERTY_NAME_COLUMN, Qt::UserRole).toInt();
+ const QString propertyValue = getData(itemIndex.row(), PROPERTY_VALUE_COLUMN, Qt::DisplayRole).toString();
ColorChooserDialog *colorDialog = new ColorChooserDialog(dialogText);
colorDialog->setItemData(objectId, propertyName, propertyValue);
diff --git a/src/plugins/qmljsinspector/qmljspropertyinspector.h b/src/plugins/qmljsinspector/qmljspropertyinspector.h
index 31fe1d61b6..3080c64652 100644
--- a/src/plugins/qmljsinspector/qmljspropertyinspector.h
+++ b/src/plugins/qmljsinspector/qmljspropertyinspector.h
@@ -33,9 +33,8 @@
#define PROPERTYINSPECTOR_H
#include <qmljsprivateapi.h>
-#include <QTreeView>
+#include <utils/basetreeview.h>
#include <QStandardItemModel>
-#include <QSortFilterProxyModel>
#include <QDialog>
#include <QDialogButtonBox>
@@ -51,21 +50,6 @@ namespace Internal {
class PropertyEditDelegate;
-class PropertiesFilter : public QSortFilterProxyModel
-{
- Q_OBJECT
-public:
- explicit PropertiesFilter(QObject *parent = 0)
- : QSortFilterProxyModel(parent)
- {
- setDynamicSortFilter(true);
- }
-
- ~PropertiesFilter() { }
-
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
-};
-
class ExpressionEdit : public QDialog
{
Q_OBJECT
@@ -108,7 +92,23 @@ private:
QmlEditorWidgets::CustomColorDialog *m_mainFrame;
};
-class QmlJSPropertyInspector : public QTreeView
+class QmlJSPropertyInspectorModel : public QStandardItemModel
+{
+ Q_OBJECT
+public:
+ QmlJSPropertyInspectorModel();
+ void setContentsValid(bool contentsValid);
+ bool contentsValid() const;
+
+protected:
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+private:
+ bool m_contentsValid;
+};
+
+class QmlJSPropertyInspector : public Utils::BaseTreeView
{
Q_OBJECT
public:
@@ -123,23 +123,24 @@ public:
explicit QmlJSPropertyInspector(QWidget *parent = 0);
void clear();
+ void setContentsValid(bool contentsValid);
+ bool contentsValid() const;
signals:
void changePropertyValue(int debugId, QString propertyName, QString valueExpression);
void customContextMenuRequested(const QPoint &pos);
public slots:
- void setCurrentObjects(const QList<QDeclarativeDebugObjectReference> &);
+ void setCurrentObjects(const QList<QmlDebugObjectReference> &);
void propertyValueEdited(const int objectId,const QString &propertyName, const QString &propertyValue);
void propertyValueChanged(int debugId, const QByteArray &propertyName, const QVariant &propertyValue);
- void filterBy(const QString &expression);
void openExpressionEditor(const QModelIndex &itemIndex);
void openColorSelector(const QModelIndex &itemIndex);
private:
friend class PropertyEditDelegate;
- void buildPropertyTree(const QDeclarativeDebugObjectReference &);
+ void buildPropertyTree(const QmlDebugObjectReference &);
void addRow(const QString &name, const QString &value, const QString &type,
const int debugId = -1, bool editable = true);
void setColorIcon(int row);
@@ -149,8 +150,7 @@ private:
void contextMenuEvent(QContextMenuEvent *ev);
- QStandardItemModel m_model;
- PropertiesFilter *m_filter;
+ QmlJSPropertyInspectorModel m_model;
QList<int> m_currentObjects;
};
diff --git a/src/plugins/qmljsinspector/qmljstoolbarcolorbox.cpp b/src/plugins/qmljsinspector/qmljstoolbarcolorbox.cpp
deleted file mode 100644
index b16fa40771..0000000000
--- a/src/plugins/qmljsinspector/qmljstoolbarcolorbox.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-#include "qmljstoolbarcolorbox.h"
-
-#include <QPixmap>
-#include <QPainter>
-#include <QMenu>
-#include <QAction>
-#include <QContextMenuEvent>
-#include <QClipboard>
-#include <QApplication>
-#include <QColorDialog>
-#include <QDrag>
-
-#include <QMimeData>
-#include <QDebug>
-
-namespace QmlJSInspector {
-
-ToolBarColorBox::ToolBarColorBox(QWidget *parent) :
- QLabel(parent)
-{
- m_color = Qt::white;
- m_borderColorOuter = Qt::white;
- m_borderColorInner = QColor(143, 143 ,143);
-
- m_copyHexColorAction = new QAction(QIcon(QLatin1String(":/qml/images/color-picker-small-hicontrast.png")), tr("Copy Color"), this);
- connect(m_copyHexColorAction, SIGNAL(triggered()), SLOT(copyColorToClipboard()));
- setScaledContents(false);
-}
-
-void ToolBarColorBox::setColor(const QColor &color)
-{
- m_color = color;
-
- QPixmap pix = createDragPixmap(width());
- setPixmap(pix);
- update();
-}
-
-void ToolBarColorBox::setInnerBorderColor(const QColor &color)
-{
- m_borderColorInner = color;
- setColor(m_color);
-}
-
-void ToolBarColorBox::setOuterBorderColor(const QColor &color)
- {
- m_borderColorOuter = color;
- setColor(m_color);
- }
-
-void ToolBarColorBox::mousePressEvent(QMouseEvent *event)
-{
- m_dragBeginPoint = event->pos();
- m_dragStarted = false;
-}
-
-void ToolBarColorBox::mouseMoveEvent(QMouseEvent *event)
-{
- if (event->buttons() & Qt::LeftButton
- && QPoint(event->pos() - m_dragBeginPoint).manhattanLength() > QApplication::startDragDistance()
- && !m_dragStarted)
- {
- m_dragStarted = true;
- QDrag *drag = new QDrag(this);
- QMimeData *mimeData = new QMimeData;
-
- mimeData->setText(m_color.name());
- drag->setMimeData(mimeData);
- drag->setPixmap(createDragPixmap());
-
- drag->exec();
- }
-}
-
-QPixmap ToolBarColorBox::createDragPixmap(int size) const
-{
- QPixmap pix(size, size);
- QPainter p(&pix);
-
- p.setBrush(QBrush(m_color));
- p.setPen(QPen(QBrush(m_borderColorInner),1));
-
- p.fillRect(0, 0, size, size, m_borderColorOuter);
- p.drawRect(1,1, size - 3, size - 3);
- return pix;
-}
-
-void ToolBarColorBox::contextMenuEvent(QContextMenuEvent *ev)
-{
- QMenu contextMenu;
- contextMenu.addAction(m_copyHexColorAction);
- contextMenu.exec(ev->globalPos());
-}
-
-void ToolBarColorBox::copyColorToClipboard()
-{
- QClipboard *clipboard = QApplication::clipboard();
- clipboard->setText(m_color.name());
-}
-
-} // namespace QmlJSInspector
diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
index e10c15e4e8..9f9f66f373 100644
--- a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
+++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
@@ -45,7 +45,7 @@
#include <cplusplus/CoreTypes.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/SimpleLexer.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -143,7 +143,12 @@ protected:
if (!templateIdentifier)
return false;
const QString callName = QString::fromUtf8(templateIdentifier->chars());
- if (callName != QLatin1String("qmlRegisterType"))
+ int argCount = 0;
+ if (callName == QLatin1String("qmlRegisterType"))
+ argCount = 4;
+ else if (callName == QLatin1String("qmlRegisterUncreatableType"))
+ argCount = 5;
+ else
return false;
// must have a single typeid template argument
@@ -154,16 +159,21 @@ protected:
if (!typeId)
return false;
- // must have four arguments
+ // must have four arguments for qmlRegisterType and five for qmlRegisterUncreatableType
if (!ast->expression_list
|| !ast->expression_list->value || !ast->expression_list->next
|| !ast->expression_list->next->value || !ast->expression_list->next->next
|| !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
- || !ast->expression_list->next->next->next->value
- || ast->expression_list->next->next->next->next)
+ || !ast->expression_list->next->next->next->value)
+ return false;
+ if (argCount == 4 && ast->expression_list->next->next->next->next)
+ return false;
+ if (argCount == 5 && (!ast->expression_list->next->next->next->next
+ || !ast->expression_list->next->next->next->next->value
+ || ast->expression_list->next->next->next->next->next))
return false;
- // last argument must be a string literal
+ // 4th argument must be a string literal
const StringLiteral *nameLit = 0;
if (StringLiteralAST *nameAst = skipStringCall(ast->expression_list->next->next->next->value)->asStringLiteral())
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
@@ -748,12 +758,17 @@ bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
if (!document->control())
return false;
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
+ const QByteArray qmlRegisterUncreatableTypeToken("qmlRegisterUncreatableType");
const QByteArray setContextPropertyToken("setContextProperty");
if (document->control()->findIdentifier(
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
return true;
}
if (document->control()->findIdentifier(
+ qmlRegisterUncreatableTypeToken.constData(), qmlRegisterUncreatableTypeToken.size())) {
+ return true;
+ }
+ if (document->control()->findIdentifier(
setContextPropertyToken.constData(), setContextPropertyToken.size())) {
return true;
}
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index 35f141539a..ea67158e79 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -40,7 +40,7 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/messagemanager.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsbind.h>
diff --git a/src/plugins/qmljstools/qmljstools.qbs b/src/plugins/qmljstools/qmljstools.qbs
index 4636611129..a62978f0e4 100644
--- a/src/plugins/qmljstools/qmljstools.qbs
+++ b/src/plugins/qmljstools/qmljstools.qbs
@@ -8,7 +8,7 @@ QtcPlugin {
Depends { name: "qt"; submodules: ['gui'] }
Depends { name: "Core" }
Depends { name: "LanguageUtils" }
- Depends { name: "CPlusPlus" }
+ Depends { name: "CppTools" }
Depends { name: "QmlJS" }
Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" }
@@ -58,7 +58,7 @@ QtcPlugin {
]
ProductModule {
- Depends { name: "CPlusPlus" }
+ Depends { name: "CppTools" }
Depends { name: "QmlJSDebugClient" }
}
}
diff --git a/src/plugins/qmljstools/qmljstools_dependencies.pri b/src/plugins/qmljstools/qmljstools_dependencies.pri
index b9b7d70c88..fe173668db 100644
--- a/src/plugins/qmljstools/qmljstools_dependencies.pri
+++ b/src/plugins/qmljstools/qmljstools_dependencies.pri
@@ -4,3 +4,4 @@ include($$IDE_SOURCE_TREE/src/libs/qmljs/qmljs.pri)
include($$IDE_SOURCE_TREE/src/plugins/projectexplorer/projectexplorer.pri)
include($$IDE_SOURCE_TREE/src/plugins/qtsupport/qtsupport.pri)
include($$IDE_SOURCE_TREE/src/plugins/texteditor/texteditor.pri)
+include($$IDE_SOURCE_TREE/src/plugins/cpptools/cpptools.pri)
diff --git a/src/plugins/qmljstools/qmljstoolsplugin.h b/src/plugins/qmljstools/qmljstoolsplugin.h
index f35917fd4d..54b8837a37 100644
--- a/src/plugins/qmljstools/qmljstoolsplugin.h
+++ b/src/plugins/qmljstools/qmljstoolsplugin.h
@@ -56,6 +56,7 @@ class ModelManager;
class QmlJSToolsPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlJSTools.json")
public:
static QmlJSToolsPlugin *instance() { return m_instance; }
diff --git a/src/plugins/qmlprofiler/abstractqmlprofilerrunner.h b/src/plugins/qmlprofiler/abstractqmlprofilerrunner.h
index ace4c28a51..78fc5cd9df 100644
--- a/src/plugins/qmlprofiler/abstractqmlprofilerrunner.h
+++ b/src/plugins/qmlprofiler/abstractqmlprofilerrunner.h
@@ -49,7 +49,7 @@ public:
virtual void start() = 0;
virtual void stop() = 0;
- virtual int debugPort() const = 0;
+ virtual quint16 debugPort() const = 0;
signals:
void started();
diff --git a/src/plugins/qmlprofiler/codaqmlprofilerrunner.cpp b/src/plugins/qmlprofiler/codaqmlprofilerrunner.cpp
index 3a1ddd1f66..d78c889a1c 100644
--- a/src/plugins/qmlprofiler/codaqmlprofilerrunner.cpp
+++ b/src/plugins/qmlprofiler/codaqmlprofilerrunner.cpp
@@ -80,7 +80,7 @@ void CodaQmlProfilerRunner::appendMessage(ProjectExplorer::RunControl *, const Q
emit appendMessage(message, format);
}
-int QmlProfiler::Internal::CodaQmlProfilerRunner::debugPort() const
+quint16 QmlProfiler::Internal::CodaQmlProfilerRunner::debugPort() const
{
return m_configuration->debuggerAspect()->qmlDebugServerPort();
}
diff --git a/src/plugins/qmlprofiler/codaqmlprofilerrunner.h b/src/plugins/qmlprofiler/codaqmlprofilerrunner.h
index 3bc86fb634..aca03e9174 100644
--- a/src/plugins/qmlprofiler/codaqmlprofilerrunner.h
+++ b/src/plugins/qmlprofiler/codaqmlprofilerrunner.h
@@ -55,7 +55,7 @@ public:
// AbstractQmlProfilerRunner
virtual void start();
virtual void stop();
- virtual int debugPort() const;
+ virtual quint16 debugPort() const;
private slots:
void appendMessage(ProjectExplorer::RunControl *, const QString &message,
diff --git a/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp b/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp
index 1d708dda59..e7b11f5cf9 100644
--- a/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp
+++ b/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp
@@ -85,7 +85,7 @@ void LocalQmlProfilerRunner::stop()
}
}
-int LocalQmlProfilerRunner::debugPort() const
+quint16 LocalQmlProfilerRunner::debugPort() const
{
return m_configuration.port;
}
diff --git a/src/plugins/qmlprofiler/localqmlprofilerrunner.h b/src/plugins/qmlprofiler/localqmlprofilerrunner.h
index c71b234f7c..c49c38c283 100644
--- a/src/plugins/qmlprofiler/localqmlprofilerrunner.h
+++ b/src/plugins/qmlprofiler/localqmlprofilerrunner.h
@@ -59,7 +59,7 @@ public:
// AbstractQmlProfilerRunner
virtual void start();
virtual void stop();
- virtual int debugPort() const;
+ virtual quint16 debugPort() const;
bool hasExecutable() const { return !m_configuration.executable.isEmpty(); }
diff --git a/src/plugins/qmlprofiler/qml/Label.qml b/src/plugins/qmlprofiler/qml/Label.qml
index 0d89c057ab..21e322d5be 100644
--- a/src/plugins/qmlprofiler/qml/Label.qml
+++ b/src/plugins/qmlprofiler/qml/Label.qml
@@ -60,17 +60,19 @@ Item {
function updateHeight() {
height = root.singleRowHeight * (1 +
- (expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex)));
+ (expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) :
+ qmlProfilerDataModel.maxNestingForType(typeIndex)));
}
function getDescriptions() {
var desc=[];
var ids=[];
var extdesc=[];
- for (var i=0; i<qmlEventList.uniqueEventsOfType(typeIndex); i++) {
- desc[i] = qmlEventList.eventTextForType(typeIndex, i);
- ids[i] = qmlEventList.eventIdForType(typeIndex, i);
- extdesc[i] = qmlEventList.eventDisplayNameForType(typeIndex, i) + " : " + desc[i];
+ for (var i=0; i<qmlProfilerDataModel.uniqueEventsOfType(typeIndex); i++) {
+ desc[i] = qmlProfilerDataModel.eventTextForType(typeIndex, i);
+ ids[i] = qmlProfilerDataModel.eventIdForType(typeIndex, i);
+ extdesc[i] = qmlProfilerDataModel.eventDisplayNameForType(typeIndex, i) +
+ " : " + desc[i];
}
descriptions = desc;
eventIds = ids;
@@ -79,18 +81,18 @@ Item {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onReloadDetailLabels: getDescriptions();
onStateChanged: {
// Empty
- if (qmlEventList.getCurrentStateFromQml() == 0) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
descriptions = [];
eventIds = [];
extdescriptions = [];
updateHeight();
} else
// Done
- if (qmlEventList.getCurrentStateFromQml() == 3) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
getDescriptions();
}
}
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index ae2b9a8122..b810a78530 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -51,11 +51,16 @@ Rectangle {
property alias selectionLocked : view.selectionLocked
signal updateLockButton
property alias selectedItem: view.selectedItem
- signal selectedEventIdChanged(int eventId)
+ signal selectedEventChanged(int eventId)
property bool lockItemSelection : false
- property variant names: [ qsTr("Painting"), qsTr("Compiling"), qsTr("Creating"), qsTr("Binding"), qsTr("Handling Signal")]
- property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
+ property variant names: [ qsTr("Painting"),
+ qsTr("Compiling"),
+ qsTr("Creating"),
+ qsTr("Binding"),
+ qsTr("Handling Signal")]
+ property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
+ "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
property variant mainviewTimePerPixel : 0
@@ -64,9 +69,6 @@ Rectangle {
property int lineNumber: -1
property int columnNumber: 0
- property real elapsedTime
- signal updateTimer
-
signal updateRangeButton
property bool selectionRangeMode: false
@@ -77,7 +79,11 @@ Rectangle {
signal changeToolTip(string text)
signal updateVerticalScroll(int newPosition)
- property bool applicationDied : false
+ property bool recordingEnabled: false
+ property bool appKilled : false
+
+ property date recordingStartDate
+ property real elapsedTime
// ***** connections with external objects
Connections {
@@ -92,7 +98,8 @@ Rectangle {
backgroundMarks.updateMarks(startTime, endTime);
view.updateFlickRange(startTime, endTime);
if (duration > 0) {
- var candidateWidth = qmlEventList.traceDuration() * flick.width / duration;
+ var candidateWidth = qmlProfilerDataModel.traceDuration() *
+ flick.width / duration;
if (flick.contentWidth !== candidateWidth)
flick.contentWidth = candidateWidth;
}
@@ -101,20 +108,21 @@ Rectangle {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onCountChanged: {
- eventCount = qmlEventList.count();
+ eventCount = qmlProfilerDataModel.count();
if (eventCount === 0)
root.clearAll();
if (eventCount > 1) {
root.progress = Math.min(1.0,
- (qmlEventList.lastTimeMark() - qmlEventList.traceStartTime()) / root.elapsedTime * 1e-9 );
+ (qmlProfilerDataModel.lastTimeMark() -
+ qmlProfilerDataModel.traceStartTime()) / root.elapsedTime * 1e-9 );
} else {
root.progress = 0;
}
}
onStateChanged: {
- switch (qmlEventList.getCurrentStateFromQml()) {
+ switch (qmlProfilerDataModel.getCurrentStateFromQml()) {
case 0: {
root.clearAll();
break;
@@ -133,7 +141,9 @@ Rectangle {
dataAvailable = true;
view.visible = true;
view.requestPaint();
- zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceStartTime() + qmlEventList.traceDuration()/10);
+ zoomControl.setRange(qmlProfilerDataModel.traceStartTime(),
+ qmlProfilerDataModel.traceStartTime() +
+ qmlProfilerDataModel.traceDuration()/10);
break;
}
}
@@ -151,7 +161,7 @@ Rectangle {
function clearData() {
view.clearData();
dataAvailable = false;
- applicationDied = false;
+ appKilled = false;
eventCount = 0;
hideRangeDetails();
selectionRangeMode = false;
@@ -166,8 +176,6 @@ Rectangle {
function clearAll() {
clearDisplay();
- root.elapsedTime = 0;
- root.updateTimer();
}
function nextEvent() {
@@ -180,9 +188,10 @@ Rectangle {
function updateWindowLength(absoluteFactor) {
var windowLength = view.endTime - view.startTime;
- if (qmlEventList.traceEndTime() <= qmlEventList.traceStartTime() || windowLength <= 0)
+ if (qmlProfilerDataModel.traceEndTime() <= qmlProfilerDataModel.traceStartTime() ||
+ windowLength <= 0)
return;
- var currentFactor = windowLength / qmlEventList.traceDuration();
+ var currentFactor = windowLength / qmlProfilerDataModel.traceDuration();
updateZoom(absoluteFactor / currentFactor);
}
@@ -193,8 +202,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlEventList.traceDuration()) {
- newWindowLength = qmlEventList.traceDuration();
+ if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
+ newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -205,7 +214,7 @@ Rectangle {
var fixedPoint = (view.startTime + view.endTime) / 2;
if (view.selectedItem !== -1) {
// center on selected item if it's inside the current screen
- var newFixedPoint = qmlEventList.getStartTime(view.selectedItem);
+ var newFixedPoint = qmlProfilerDataModel.getStartTime(view.selectedItem);
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
fixedPoint = newFixedPoint;
}
@@ -222,8 +231,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlEventList.traceDuration()) {
- newWindowLength = qmlEventList.traceDuration();
+ if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
+ newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -241,8 +250,8 @@ Rectangle {
var newStart = Math.floor(centerPoint - windowLength/2);
if (newStart < 0)
newStart = 0;
- if (newStart + windowLength > qmlEventList.traceEndTime())
- newStart = qmlEventList.traceEndTime() - windowLength;
+ if (newStart + windowLength > qmlProfilerDataModel.traceEndTime())
+ newStart = qmlProfilerDataModel.traceEndTime() - windowLength;
zoomControl.setRange(newStart, newStart + windowLength);
}
@@ -252,17 +261,16 @@ Rectangle {
return;
// if item is outside of the view, jump back to its position
- if (qmlEventList.getEndTime(itemIndex) < view.startTime || qmlEventList.getStartTime(itemIndex) > view.endTime) {
- recenter((qmlEventList.getStartTime(itemIndex) + qmlEventList.getEndTime(itemIndex)) / 2);
+ if (qmlProfilerDataModel.getEndTime(itemIndex) < view.startTime ||
+ qmlProfilerDataModel.getStartTime(itemIndex) > view.endTime) {
+ recenter((qmlProfilerDataModel.getStartTime(itemIndex) +
+ qmlProfilerDataModel.getEndTime(itemIndex)) / 2);
}
}
- function globalZoom() {
- zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceEndTime());
- }
-
function wheelZoom(wheelCenter, wheelDelta) {
- if (qmlEventList.traceEndTime() > qmlEventList.traceStartTime() && wheelDelta !== 0) {
+ if (qmlProfilerDataModel.traceEndTime() > qmlProfilerDataModel.traceStartTime() &&
+ wheelDelta !== 0) {
if (wheelDelta>0)
updateZoomCentered(wheelCenter, 1/1.2);
else
@@ -311,34 +319,22 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem != -1 && !lockItemSelection) {
lockItemSelection = true;
- selectedEventIdChanged( qmlEventList.getEventId(selectedItem) );
+ selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) );
lockItemSelection = false;
}
}
- // ***** child items
- Timer {
- id: elapsedTimer
- property date startDate
- property bool reset: true
- running: connection ? (connection.recording && connection.enabled) : false
- repeat: true
- onRunningChanged: {
- if (running) reset = true;
- }
- interval: 100
- triggeredOnStart: true
- onTriggered: {
- if (reset) {
- startDate = new Date();
- reset = false;
- }
- var time = (new Date() - startDate)/1000;
- root.elapsedTime = time.toFixed(1);
- root.updateTimer();
+ onRecordingEnabledChanged: {
+ if (recordingEnabled) {
+ recordingStartDate = new Date();
+ elapsedTime = 0;
+ } else {
+ elapsedTime = (new Date() - recordingStartDate)/1000.0;
}
}
+
+ // ***** child items
TimeMarks {
id: backgroundMarks
y: labels.y
@@ -380,7 +376,8 @@ Rectangle {
selectionRange.isDragging = false;
}
onDoubleClicked: {
- zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration);
+ zoomControl.setRange(selectionRange.startTime,
+ selectionRange.startTime + selectionRange.duration);
root.selectionRangeMode = false;
root.updateRangeButton();
}
@@ -394,10 +391,10 @@ Rectangle {
z: 2
}
- TimelineView {
+ TimelineRenderer {
id: view
- eventList: qmlEventList
+ profilerDataModel: qmlProfilerDataModel
x: flick.contentX
width: flick.width
@@ -405,9 +402,13 @@ Rectangle {
property variant startX: 0
onStartXChanged: {
- var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
+ var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
+ qmlProfilerDataModel.traceStartTime();
if (Math.abs(newStartTime - startTime) > 1) {
- var newEndTime = Math.round((startX+flick.width)* (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
+ var newEndTime = Math.round((startX+flick.width) *
+ (endTime - startTime) /
+ flick.width) +
+ qmlProfilerDataModel.traceStartTime();
zoomControl.setRange(newStartTime, newEndTime);
}
@@ -419,7 +420,8 @@ Rectangle {
if (start !== startTime || end !== endTime) {
startTime = start;
endTime = end;
- var newStartX = (startTime - qmlEventList.traceStartTime()) * flick.width / (endTime-startTime);
+ var newStartX = (startTime - qmlProfilerDataModel.traceStartTime()) *
+ flick.width / (endTime-startTime);
if (Math.abs(newStartX - startX) >= 1)
startX = newStartX;
}
@@ -428,24 +430,25 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem !== -1) {
// display details
- rangeDetails.duration = qmlEventList.getDuration(selectedItem)/1000.0;
- rangeDetails.label = qmlEventList.getDetails(selectedItem);
- rangeDetails.file = qmlEventList.getFilename(selectedItem);
- rangeDetails.line = qmlEventList.getLine(selectedItem);
- rangeDetails.column = qmlEventList.getColumn(selectedItem);
- rangeDetails.type = root.names[qmlEventList.getType(selectedItem)];
- rangeDetails.isBindingLoop = qmlEventList.getBindingLoopDest(selectedItem)!==-1;
+ rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0;
+ rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem);
+ rangeDetails.file = qmlProfilerDataModel.getFilename(selectedItem);
+ rangeDetails.line = qmlProfilerDataModel.getLine(selectedItem);
+ rangeDetails.column = qmlProfilerDataModel.getColumn(selectedItem);
+ rangeDetails.type = root.names[qmlProfilerDataModel.getType(selectedItem)];
+ rangeDetails.isBindingLoop = qmlProfilerDataModel.getBindingLoopDest(selectedItem)!==-1;
rangeDetails.visible = true;
// center view (horizontally)
var windowLength = view.endTime - view.startTime;
- var eventStartTime = qmlEventList.getStartTime(selectedItem);
- var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedItem);
+ var eventStartTime = qmlProfilerDataModel.getStartTime(selectedItem);
+ var eventEndTime = eventStartTime +
+ qmlProfilerDataModel.getDuration(selectedItem);
if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
var center = (eventStartTime + eventEndTime)/2;
- var from = Math.min(qmlEventList.traceEndTime()-windowLength,
+ var from = Math.min(qmlProfilerDataModel.traceEndTime()-windowLength,
Math.max(0, Math.floor(center - windowLength/2)));
zoomControl.setRange(from, from + windowLength);
@@ -456,8 +459,10 @@ Rectangle {
if (itemY < root.scrollY) {
root.updateVerticalScroll(itemY);
} else
- if (itemY + root.singleRowHeight > root.scrollY + root.candidateHeight) {
- root.updateVerticalScroll(itemY + root.singleRowHeight - root.candidateHeight);
+ if (itemY + root.singleRowHeight >
+ root.scrollY + root.candidateHeight) {
+ root.updateVerticalScroll(itemY + root.singleRowHeight -
+ root.candidateHeight);
}
} else {
root.hideRangeDetails();
@@ -466,14 +471,18 @@ Rectangle {
onItemPressed: {
if (pressedItem !== -1) {
- root.gotoSourceLocation(qmlEventList.getFilename(pressedItem), qmlEventList.getLine(pressedItem), qmlEventList.getColumn(pressedItem));
+ root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem),
+ qmlProfilerDataModel.getLine(pressedItem),
+ qmlProfilerDataModel.getColumn(pressedItem));
}
}
// hack to pass mouse events to the other mousearea if enabled
- startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x : -flick.contentX
+ startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x :
+ -flick.contentX
endDragArea: selectionRangeDrag.enabled ?
- selectionRangeDrag.x + selectionRangeDrag.width : -flick.contentX-1
+ selectionRangeDrag.x + selectionRangeDrag.width :
+ -flick.contentX-1
}
MouseArea {
id: selectionRangeControl
diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js
index 4fe625a7fd..821f344653 100644
--- a/src/plugins/qmlprofiler/qml/Overview.js
+++ b/src/plugins/qmlprofiler/qml/Overview.js
@@ -32,7 +32,7 @@
.pragma library
-var qmlEventList = 0;
+var qmlProfilerDataModel = 0;
//draw background of the graph
function drawGraph(canvas, ctxt, region)
@@ -44,7 +44,7 @@ function drawGraph(canvas, ctxt, region)
//draw the actual data to be graphed
function drawData(canvas, ctxt, region)
{
- if ((!qmlEventList) || qmlEventList.count() == 0)
+ if ((!qmlProfilerDataModel) || qmlProfilerDataModel.count() == 0)
return;
var typeCount = 5;
@@ -53,17 +53,18 @@ function drawData(canvas, ctxt, region)
var height = canvas.height - bump;
var blockHeight = height / typeCount;
- var spacing = width / qmlEventList.traceDuration();
+ var spacing = width / qmlProfilerDataModel.traceDuration();
var highest = [0,0,0,0,0]; // note: change if typeCount changes
- for (var ii = 0; ii < qmlEventList.count(); ++ii) {
+ for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
- var xx = (qmlEventList.getStartTime(ii) - qmlEventList.traceStartTime()) * spacing;
+ var xx = (qmlProfilerDataModel.getStartTime(ii) -
+ qmlProfilerDataModel.traceStartTime()) * spacing;
if (xx > region.x + region.width)
continue;
- var eventWidth = qmlEventList.getDuration(ii) * spacing;
+ var eventWidth = qmlProfilerDataModel.getDuration(ii) * spacing;
if (xx + eventWidth < region.x)
continue;
@@ -71,24 +72,26 @@ function drawData(canvas, ctxt, region)
eventWidth = 1;
xx = Math.round(xx);
- var ty = qmlEventList.getType(ii);
+ var ty = qmlProfilerDataModel.getType(ii);
if (xx + eventWidth > highest[ty]) {
// special: animations
- if (ty === 0 && qmlEventList.getAnimationCount(ii) >= 0) {
- var vertScale = qmlEventList.getMaximumAnimationCount() - qmlEventList.getMinimumAnimationCount();
+ if (ty === 0 && qmlProfilerDataModel.getAnimationCount(ii) >= 0) {
+ var vertScale = qmlProfilerDataModel.getMaximumAnimationCount() -
+ qmlProfilerDataModel.getMinimumAnimationCount();
if (vertScale < 1)
vertScale = 1;
- var fraction = (qmlEventList.getAnimationCount(ii) - qmlEventList.getMinimumAnimationCount()) / vertScale;
+ var fraction = (qmlProfilerDataModel.getAnimationCount(ii) -
+ qmlProfilerDataModel.getMinimumAnimationCount()) / vertScale;
var eventHeight = blockHeight * (fraction * 0.85 + 0.15);
var yy = bump + ty*blockHeight + blockHeight - eventHeight;
- var fpsFraction = qmlEventList.getFramerate(ii) / 60.0;
+ var fpsFraction = qmlProfilerDataModel.getFramerate(ii) / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)";
ctxt.fillRect(xx, yy, eventWidth, eventHeight);
} else {
- var hue = ( qmlEventList.getEventId(ii) * 25 ) % 360;
+ var hue = ( qmlProfilerDataModel.getEventId(ii) * 25 ) % 360;
ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
}
@@ -100,12 +103,13 @@ function drawData(canvas, ctxt, region)
ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2;
var radius = 1;
- for (var ii = 0; ii < qmlEventList.count(); ++ii) {
- if (qmlEventList.getBindingLoopDest(ii) >= 0) {
- var xcenter = Math.round(qmlEventList.getStartTime(ii) +
- qmlEventList.getDuration(ii) -
- qmlEventList.traceStartTime()) * spacing;
- var ycenter = Math.round(bump + qmlEventList.getType(ii) * blockHeight + blockHeight/2);
+ for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
+ if (qmlProfilerDataModel.getBindingLoopDest(ii) >= 0) {
+ var xcenter = Math.round(qmlProfilerDataModel.getStartTime(ii) +
+ qmlProfilerDataModel.getDuration(ii) -
+ qmlProfilerDataModel.traceStartTime()) * spacing;
+ var ycenter = Math.round(bump + qmlProfilerDataModel.getType(ii) *
+ blockHeight + blockHeight/2);
ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
ctxt.stroke();
}
@@ -114,19 +118,20 @@ function drawData(canvas, ctxt, region)
function drawTimeBar(canvas, ctxt, region)
{
- if (!qmlEventList)
+ if (!qmlProfilerDataModel)
return;
var width = canvas.width;
var height = 10;
- var startTime = qmlEventList.traceStartTime();
- var endTime = qmlEventList.traceEndTime();
+ var startTime = qmlProfilerDataModel.traceStartTime();
+ var endTime = qmlProfilerDataModel.traceEndTime();
- var totalTime = qmlEventList.traceDuration();
+ var totalTime = qmlProfilerDataModel.traceDuration();
var spacing = width / totalTime;
var initialBlockLength = 120;
- var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
+ var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width *
+ initialBlockLength ) / Math.LN2 ) );
var pixelsPerBlock = timePerBlock * spacing;
var pixelsPerSection = pixelsPerBlock / 5;
var blockCount = width / pixelsPerBlock;
diff --git a/src/plugins/qmlprofiler/qml/Overview.qml b/src/plugins/qmlprofiler/qml/Overview.qml
index 5526502169..c82277bc26 100644
--- a/src/plugins/qmlprofiler/qml/Overview.qml
+++ b/src/plugins/qmlprofiler/qml/Overview.qml
@@ -51,8 +51,8 @@ Canvas2D {
}
function updateRange() {
- var newStartTime = Math.round(rangeMover.x * qmlEventList.traceDuration() / width) + qmlEventList.traceStartTime();
- var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlEventList.traceDuration() / width) + qmlEventList.traceStartTime();
+ var newStartTime = Math.round(rangeMover.x * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
+ var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
if (startTime !== newStartTime || endTime !== newEndTime) {
zoomControl.setRange(newStartTime, newEndTime);
}
@@ -62,13 +62,13 @@ Canvas2D {
Connections {
target: zoomControl
onRangeChanged: {
- if (qmlEventList) {
+ if (qmlProfilerDataModel) {
startTime = zoomControl.startTime();
endTime = zoomControl.endTime();
- var newRangeX = (startTime - qmlEventList.traceStartTime()) * width / qmlEventList.traceDuration();
+ var newRangeX = (startTime - qmlProfilerDataModel.traceStartTime()) * width / qmlProfilerDataModel.traceDuration();
if (rangeMover.x !== newRangeX)
rangeMover.x = newRangeX;
- var newWidth = (endTime-startTime) * width / qmlEventList.traceDuration();
+ var newWidth = (endTime-startTime) * width / qmlProfilerDataModel.traceDuration();
if (rangeMover.width !== newWidth)
rangeMover.width = newWidth;
}
@@ -76,10 +76,10 @@ Canvas2D {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onStateChanged: {
// State is "done"
- if (qmlEventList.getCurrentStateFromQml() == 3) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
dataAvailable = true;
requestRedraw();
}
@@ -88,7 +88,7 @@ Canvas2D {
// ***** slots
onDrawRegion: {
- Plotter.qmlEventList = qmlEventList;
+ Plotter.qmlProfilerDataModel = qmlProfilerDataModel;
if (dataAvailable) {
Plotter.plot(canvas, ctxt, region);
} else {
diff --git a/src/plugins/qmlprofiler/qml/SelectionRange.qml b/src/plugins/qmlprofiler/qml/SelectionRange.qml
index 616fd89598..b8b6507258 100644
--- a/src/plugins/qmlprofiler/qml/SelectionRange.qml
+++ b/src/plugins/qmlprofiler/qml/SelectionRange.qml
@@ -50,7 +50,7 @@ Rectangle {
property string endTimeString: detailedPrintTime(startTime+duration)
property string durationString: detailedPrintTime(duration)
- property variant startTime: x * selectionRange.viewTimePerPixel + qmlEventList.traceStartTime()
+ property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerDataModel.traceStartTime()
property variant duration: width * selectionRange.viewTimePerPixel
property variant viewTimePerPixel: 1
property variant creationState : 0
diff --git a/src/plugins/qmlprofiler/qml/StatusDisplay.qml b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
index d4507a37d6..8919d07b13 100644
--- a/src/plugins/qmlprofiler/qml/StatusDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
@@ -69,7 +69,7 @@ Item {
states: [
// no data available
State {
- when: (root.eventCount == 0) && !elapsedTimer.running
+ when: (root.eventCount == 0) && !root.recordingEnabled
PropertyChanges {
target: statusDisplay
visible: true
@@ -85,7 +85,7 @@ Item {
},
// running app
State {
- when: elapsedTimer.running
+ when: root.recordingEnabled
PropertyChanges {
target: statusDisplay
visible: true
@@ -99,7 +99,7 @@ Item {
// loading data
State {
name: "loading"
- when: (!root.dataAvailable) && (root.eventCount > 0) && !root.applicationDied
+ when: !root.dataAvailable && (root.eventCount > 0) && !root.appKilled
PropertyChanges {
target: statusDisplay
visible: true
@@ -118,7 +118,7 @@ Item {
// application died
State {
name: "deadApp"
- when: (!root.dataAvailable) && (root.eventCount > 0) && root.applicationDied
+ when: !root.dataAvailable && (root.eventCount > 0) && root.appKilled
PropertyChanges {
target: statusDisplay
visible: true
diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml
index 2cb176c0a1..d38e4de741 100644
--- a/src/plugins/qmlprofiler/qml/TimeMarks.qml
+++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml
@@ -91,15 +91,15 @@ Canvas2D {
// gray off out-of-bounds areas
var rectWidth;
- if (startTime < qmlEventList.traceStartTime()) {
+ if (startTime < qmlProfilerDataModel.traceStartTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- rectWidth = (qmlEventList.traceStartTime() - startTime) * spacing;
+ rectWidth = (qmlProfilerDataModel.traceStartTime() - startTime) * spacing;
ctxt.fillRect(0, 0, rectWidth, height);
}
- if (endTime > qmlEventList.traceEndTime()) {
+ if (endTime > qmlProfilerDataModel.traceEndTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- var rectX = (qmlEventList.traceEndTime() - startTime) * spacing;
- rectWidth = (endTime - qmlEventList.traceEndTime()) * spacing;
+ var rectX = (qmlProfilerDataModel.traceEndTime() - startTime) * spacing;
+ rectWidth = (endTime - qmlProfilerDataModel.traceEndTime()) * spacing;
ctxt.fillRect(rectX, 0, rectWidth, height);
}
}
@@ -126,8 +126,8 @@ Canvas2D {
var cumulatedHeight = 0;
for (var i=0; i<labels.rowCount; i++) {
cumulatedHeight += root.singleRowHeight + (labels.rowExpanded[i] ?
- qmlEventList.uniqueEventsOfType(i) * root.singleRowHeight :
- qmlEventList.maxNestingForType(i) * root.singleRowHeight);
+ qmlProfilerDataModel.uniqueEventsOfType(i) * root.singleRowHeight :
+ qmlProfilerDataModel.maxNestingForType(i) * root.singleRowHeight);
ctxt.strokeStyle = "#B0B0B0";
ctxt.beginPath();
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index 2f41d50db1..4dc919bef2 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -3,7 +3,12 @@ TARGET = QmlProfiler
DEFINES += PROFILER_LIBRARY
-QT += network script declarative
+QT += network script
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
include(../../qtcreatorplugin.pri)
include(../../plugins/coreplugin/coreplugin.pri)
@@ -19,14 +24,19 @@ SOURCES += \
qmlprofilerplugin.cpp \
qmlprofilertool.cpp \
qmlprofilerengine.cpp \
- tracewindow.cpp \
- timelineview.cpp \
qmlprofilerattachdialog.cpp \
localqmlprofilerrunner.cpp \
codaqmlprofilerrunner.cpp \
remotelinuxqmlprofilerrunner.cpp \
qmlprofilereventview.cpp \
- qmlprofilerdetailsrewriter.cpp
+ qmlprofilerdetailsrewriter.cpp \
+ qmlprofilertraceview.cpp \
+ timelinerenderer.cpp \
+ qmlprofilerstatemanager.cpp \
+ qv8profilerdatamodel.cpp \
+ qmlprofilerdatamodel.cpp \
+ qmlprofilerclientmanager.cpp \
+ qmlprofilerviewmanager.cpp
HEADERS += \
qmlprofilerconstants.h \
@@ -34,22 +44,26 @@ HEADERS += \
qmlprofilerplugin.h \
qmlprofilertool.h \
qmlprofilerengine.h \
- tracewindow.h \
- timelineview.h \
qmlprofilerattachdialog.h \
abstractqmlprofilerrunner.h \
localqmlprofilerrunner.h \
codaqmlprofilerrunner.h \
remotelinuxqmlprofilerrunner.h \
qmlprofilereventview.h \
- qmlprofilerdetailsrewriter.h
+ qmlprofilerdetailsrewriter.h \
+ qmlprofilertraceview.h \
+ timelinerenderer.h \
+ qmlprofilerstatemanager.h \
+ qv8profilerdatamodel.h \
+ qmlprofilerdatamodel.h \
+ qmlprofilerclientmanager.h \
+ qmlprofilerviewmanager.h
RESOURCES += \
qml/qmlprofiler.qrc
OTHER_FILES += \
qml/Detail.qml \
- qml/Elapsed.qml \
qml/Label.qml \
qml/MainView.qml \
qml/RangeDetails.qml \
@@ -59,8 +73,7 @@ OTHER_FILES += \
qml/StatusDisplay.qml \
qml/SelectionRange.qml \
qml/SelectionRangeDetails.qml \
- qml/Overview.qml \
- qml/Overview.js
+ qml/Overview.qml
FORMS += \
qmlprofilerattachdialog.ui
diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs
index 13cbca838c..f4af4e4366 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/src/plugins/qmlprofiler/qmlprofiler.qbs
@@ -35,27 +35,37 @@ QtcPlugin {
"codaqmlprofilerrunner.h",
"localqmlprofilerrunner.cpp",
"localqmlprofilerrunner.h",
- "qmlprofiler_global.h",
"qmlprofilerattachdialog.cpp",
"qmlprofilerattachdialog.h",
"qmlprofilerattachdialog.ui",
+ "qmlprofilerclientmanager.cpp",
+ "qmlprofilerclientmanager.h",
"qmlprofilerconstants.h",
+ "qmlprofilerdatamodel.cpp",
+ "qmlprofilerdatamodel.h",
"qmlprofilerdetailsrewriter.cpp",
"qmlprofilerdetailsrewriter.h",
"qmlprofilerengine.cpp",
"qmlprofilerengine.h",
"qmlprofilereventview.cpp",
"qmlprofilereventview.h",
+ "qmlprofiler_global.h",
"qmlprofilerplugin.cpp",
"qmlprofilerplugin.h",
+ "qmlprofilerstatemanager.cpp",
+ "qmlprofilerstatemanager.h",
"qmlprofilertool.cpp",
"qmlprofilertool.h",
+ "qmlprofilertraceview.cpp",
+ "qmlprofilertraceview.h",
+ "qmlprofilerviewmanager.cpp",
+ "qmlprofilerviewmanager.h",
+ "qv8profilerdatamodel.cpp",
+ "qv8profilerdatamodel.h",
"remotelinuxqmlprofilerrunner.cpp",
"remotelinuxqmlprofilerrunner.h",
- "timelineview.cpp",
- "timelineview.h",
- "tracewindow.cpp",
- "tracewindow.h",
+ "timelinerenderer.cpp",
+ "timelinerenderer.h",
"canvas/qdeclarativecanvas.cpp",
"canvas/qdeclarativecanvas_p.h",
"canvas/qdeclarativecanvastimer.cpp",
@@ -75,7 +85,8 @@ QtcPlugin {
"qml/StatusDisplay.qml",
"qml/TimeDisplay.qml",
"qml/TimeMarks.qml",
- "qml/qmlprofiler.qrc"
+ "qml/qmlprofiler.qrc",
+ "qml/Overview.js"
]
}
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
new file mode 100644
index 0000000000..a9002aa85b
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
@@ -0,0 +1,433 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerplugin.h"
+
+#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include <qmljsdebugclient/qmlprofilertraceclient.h>
+#include <qmljsdebugclient/qv8profilerclient.h>
+
+#include <utils/qtcassert.h>
+#include <QWeakPointer>
+#include <QTimer>
+#include <QMessageBox>
+
+using namespace QmlJsDebugClient;
+using namespace Core;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager::QmlProfilerClientManagerPrivate {
+public:
+ QmlProfilerClientManagerPrivate(QmlProfilerClientManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerStateManager* profilerState;
+
+ QDeclarativeDebugConnection *connection;
+ QWeakPointer<QmlProfilerTraceClient> qmlclientplugin;
+ QWeakPointer<QV8ProfilerClient> v8clientplugin;
+
+ QTimer connectionTimer;
+ int connectionAttempts;
+
+ enum ConnectMode {
+ TcpConnection, OstConnection
+ };
+ ConnectMode connectMode;
+ QString tcpHost;
+ quint64 tcpPort;
+ QString ostDevice;
+ QString sysroot;
+
+ bool v8DataReady;
+ bool qmlDataReady;
+};
+
+QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
+ QObject(parent), d(new QmlProfilerClientManagerPrivate(this))
+{
+ setObjectName("QML Profiler Connections");
+
+ d->profilerState = 0;
+
+ d->connection = 0;
+ d->connectionAttempts = 0;
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+
+ d->connectionTimer.setInterval(200);
+ connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
+}
+
+QmlProfilerClientManager::~QmlProfilerClientManager()
+{
+ disconnectClientSignals();
+ delete d->connection;
+ delete d->qmlclientplugin.data();
+ delete d->v8clientplugin.data();
+
+ delete d;
+}
+////////////////////////////////////////////////////////////////
+// Interface
+void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::TcpConnection;
+ d->tcpHost = host;
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::setOstConnection(QString ostDevice)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::OstConnection;
+ d->ostDevice = ostDevice;
+}
+
+void QmlProfilerClientManager::clearBufferedData()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->clearData();
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->clearData();
+}
+
+void QmlProfilerClientManager::discardPendingData()
+{
+ if (d->connection)
+ d->connection->flush();
+ clearBufferedData();
+}
+
+////////////////////////////////////////////////////////////////
+// Internal
+void QmlProfilerClientManager::connectClient(quint16 port)
+{
+ if (d->connection)
+ delete d->connection;
+ d->connection = new QDeclarativeDebugConnection;
+ enableServices();
+ connect(d->connection, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ this, SLOT(connectionStateChanged()));
+ d->connectionTimer.start();
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::enableServices()
+{
+ QTC_ASSERT(d->profilerState, return);
+
+ disconnectClientSignals();
+ d->profilerState->setServerRecording(false); // false by default (will be set to true when connected)
+ delete d->qmlclientplugin.data();
+ d->qmlclientplugin = new QmlProfilerTraceClient(d->connection);
+ delete d->v8clientplugin.data();
+ d->v8clientplugin = new QV8ProfilerClient(d->connection);
+ connectClientSignals();
+}
+
+void QmlProfilerClientManager::connectClientSignals()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->qmlclientplugin) {
+ connect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ connect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ connect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ connect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::disconnectClientSignals()
+{
+ if (d->qmlclientplugin) {
+ disconnect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ disconnect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ disconnect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ disconnect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::connectToClient()
+{
+ if (!d->connection || d->connection->state() != QAbstractSocket::UnconnectedState)
+ return;
+
+ if (d->connectMode == QmlProfilerClientManagerPrivate::TcpConnection) {
+ QmlProfilerTool::logStatus(QString("QML Profiler: Connecting to %1:%2 ...").arg(d->tcpHost, QString::number(d->tcpPort)));
+ d->connection->connectToHost(d->tcpHost, d->tcpPort);
+ } else {
+ QmlProfilerTool::logStatus(QString("QML Profiler: Connecting to %1 ...").arg(d->tcpHost));
+ d->connection->connectToOst(d->ostDevice);
+ }
+}
+
+void QmlProfilerClientManager::disconnectClient()
+{
+ // this might be actually be called indirectly by QDDConnectionPrivate::readyRead(), therefore allow
+ // method to complete before deleting object
+ if (d->connection) {
+ d->connection->deleteLater();
+ d->connection = 0;
+ }
+}
+
+void QmlProfilerClientManager::tryToConnect()
+{
+ ++d->connectionAttempts;
+
+ if (d->connection && d->connection->isConnected()) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+ } else if (d->connectionAttempts == 50) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+
+ QMessageBox *infoBox = QmlProfilerTool::requestMessageBox();
+ infoBox->setIcon(QMessageBox::Critical);
+ infoBox->setWindowTitle(tr("Qt Creator"));
+ infoBox->setText(tr("Could not connect to the in-process QML profiler.\n"
+ "Do you want to retry?"));
+ infoBox->setStandardButtons(QMessageBox::Retry |
+ QMessageBox::Cancel |
+ QMessageBox::Help);
+ infoBox->setDefaultButton(QMessageBox::Retry);
+ infoBox->setModal(true);
+
+ connect(infoBox, SIGNAL(finished(int)),
+ this, SLOT(retryMessageBoxFinished(int)));
+
+ infoBox->show();
+ } else {
+ connectToClient();
+ }
+}
+
+void QmlProfilerClientManager::connectionStateChanged()
+{
+ if (!d->connection)
+ return;
+ switch (d->connection->state()) {
+ case QAbstractSocket::UnconnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: disconnected");
+ break;
+ }
+ case QAbstractSocket::HostLookupState:
+ break;
+ case QAbstractSocket::ConnectingState: {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: Connecting to debug server ...");
+ break;
+ }
+ case QAbstractSocket::ConnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: connected and running");
+ // notify the client recording status
+ clientRecordingChanged();
+ break;
+ }
+ case QAbstractSocket::ClosingState:
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: closing ...");
+ break;
+ case QAbstractSocket::BoundState:
+ case QAbstractSocket::ListeningState:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::retryMessageBoxFinished(int result)
+{
+ switch (result) {
+ case QMessageBox::Retry: {
+ d->connectionAttempts = 0;
+ d->connectionTimer.start();
+ break;
+ }
+ case QMessageBox::Help: {
+ QmlProfilerTool::handleHelpRequest(QString("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
+ // fall through
+ }
+ default: {
+ if (d->connection) {
+ QmlProfilerTool::logStatus("QML Profiler: Failed to connect! " + d->connection->errorString());
+ } else {
+ QmlProfilerTool::logStatus("QML Profiler: Failed to connect!");
+ }
+
+ emit connectionFailed();
+ break;
+ }
+ }
+}
+
+void QmlProfilerClientManager::qmlComplete()
+{
+ d->qmlDataReady = true;
+ if (!d->v8clientplugin || d->v8clientplugin.data()->status() != QDeclarativeDebugClient::Enabled || d->v8DataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->qmlDataReady = false;
+ d->v8DataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::v8Complete()
+{
+ d->v8DataReady = true;
+ if (!d->qmlclientplugin || d->qmlclientplugin.data()->status() != QDeclarativeDebugClient::Enabled || d->qmlDataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::stopClientsRecording()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(false);
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(false);
+}
+
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerClientManager::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ if (d->profilerState) {
+ disconnect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ disconnect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ disconnect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+
+ d->profilerState = profilerState;
+
+ // connect
+ if (d->profilerState) {
+ connect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+}
+
+void QmlProfilerClientManager::profilerStateChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ switch (d->profilerState->currentState()) {
+ case QmlProfilerStateManager::AppStopRequested :
+ if (d->profilerState->serverRecording()) {
+ stopClientsRecording();
+ }
+ else
+ d->profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::clientRecordingChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(d->profilerState->clientRecording());
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(d->profilerState->clientRecording());
+ }
+}
+
+void QmlProfilerClientManager::serverRecordingChanged()
+{
+ if (d->profilerState->serverRecording()) {
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
new file mode 100644
index 0000000000..0ee061993a
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
@@ -0,0 +1,103 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILERCLIENTMANAGER_H
+#define QMLPROFILERCLIENTMANAGER_H
+
+#include <QObject>
+#include <QStringList>
+
+#include "qmlprofilerstatemanager.h"
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerClientManager(QObject *parent = 0);
+ ~QmlProfilerClientManager();
+
+ void registerProfilerStateManager(QmlProfilerStateManager *profilerState);
+
+ void setTcpConnection(QString host, quint64 port);
+ void setOstConnection(QString ostDevice);
+
+ void clearBufferedData();
+ void discardPendingData();
+
+signals:
+ void connectionFailed();
+
+ // data
+ void addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation);
+ void addV8Event(int,QString,QString,int,double,double);
+ void addFrameEvent(qint64,int,int);
+ void traceStarted(qint64);
+ void traceFinished(qint64);
+ void dataReadyForProcessing();
+
+public slots:
+ void connectClient(quint16 port);
+ void disconnectClient();
+
+private slots:
+ void tryToConnect();
+ void connectionStateChanged();
+ void retryMessageBoxFinished(int result);
+
+ void qmlComplete();
+ void v8Complete();
+
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+private:
+ class QmlProfilerClientManagerPrivate;
+ QmlProfilerClientManagerPrivate *d;
+
+ void connectToClient();
+
+ void enableServices();
+ void connectClientSignals();
+ void disconnectClientSignals();
+
+ void stopClientsRecording();
+};
+
+}
+}
+
+#endif // QMLPROFILERCLIENTMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerconstants.h b/src/plugins/qmlprofiler/qmlprofilerconstants.h
index 38a47c11f1..fec1b6b539 100644
--- a/src/plugins/qmlprofiler/qmlprofilerconstants.h
+++ b/src/plugins/qmlprofiler/qmlprofilerconstants.h
@@ -37,6 +37,7 @@ namespace QmlProfiler {
namespace Constants {
const char ATTACH[] = "Menu.Analyzer.Attach";
+const char TraceFileExtension[] = ".qtd";
} // namespace Constants
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
new file mode 100644
index 0000000000..95d44dca63
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
@@ -0,0 +1,1667 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerdatamodel.h"
+
+#include <QUrl>
+#include <QHash>
+#include <QtAlgorithms>
+#include <QString>
+#include <QStringList>
+
+#include <QFile>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+#include <QTimer>
+#include <utils/qtcassert.h>
+
+using namespace QmlJsDebugClient;
+
+namespace QmlProfiler {
+namespace Internal {
+
+///////////////////////////////////////////////////////////
+QmlRangeEventData::QmlRangeEventData()
+{
+ eventType = MaximumQmlEventType;
+ eventId = -1;
+ duration = 0;
+ calls = 0;
+ minTime = 0;
+ maxTime = 0;
+ timePerCall = 0;
+ percentOfTime = 0;
+ medianTime = 0;
+ isBindingLoop = false;
+}
+
+QmlRangeEventData::~QmlRangeEventData()
+{
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+}
+
+QmlRangeEventData &QmlRangeEventData::operator=(const QmlRangeEventData &ref)
+{
+ if (this == &ref)
+ return *this;
+
+ displayName = ref.displayName;
+ location = ref.location;
+ eventHashStr = ref.eventHashStr;
+ details = ref.details;
+ eventType = ref.eventType;
+ duration = ref.duration;
+ calls = ref.calls;
+ minTime = ref.minTime;
+ maxTime = ref.maxTime;
+ timePerCall = ref.timePerCall;
+ percentOfTime = ref.percentOfTime;
+ medianTime = ref.medianTime;
+ eventId = ref.eventId;
+ isBindingLoop = ref.isBindingLoop;
+
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ foreach (const QString &key, ref.parentHash.keys()) {
+ parentHash.insert(key, new QmlRangeEventRelative(ref.parentHash.value(key)));
+ }
+
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+ foreach (const QString &key, ref.childrenHash.keys()) {
+ childrenHash.insert(key, new QmlRangeEventRelative(ref.childrenHash.value(key)));
+ }
+
+ return *this;
+}
+
+///////////////////////////////////////////////////////////
+
+// endtimedata
+struct QmlRangeEventEndInstance {
+ qint64 endTime;
+ int startTimeIndex;
+ QmlRangeEventData *description;
+};
+
+// starttimedata
+struct QmlRangeEventStartInstance {
+ qint64 startTime;
+ qint64 duration;
+ qint64 level;
+ int endTimeIndex;
+ qint64 nestingLevel;
+ qint64 nestingDepth;
+ QmlRangeEventData *statsInfo;
+
+ int baseEventIndex;
+
+ // animation-related data
+ int frameRate;
+ int animationCount;
+
+ int bindingLoopHead;
+};
+
+struct QmlRangeEventTypeCount {
+ QVector<int> eventIds;
+ int nestingCount;
+};
+
+// used by quicksort
+bool compareEndTimes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
+{
+ return t1.endTime < t2.endTime;
+}
+
+bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool compareStartIndexes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
+{
+ return t1.startTimeIndex < t2.startTimeIndex;
+}
+
+//////////////////////////////////////////////////////////////
+class QmlProfilerDataModel::QmlProfilerDataModelPrivate
+{
+public:
+ QmlProfilerDataModelPrivate(QmlProfilerDataModel *qq) : q(qq) {}
+
+ QmlProfilerDataModel *q;
+
+ // convenience functions
+ void clearQmlRootEvent();
+ void insertQmlRootEvent();
+ void postProcess();
+ void sortEndTimes();
+ void findAnimationLimits();
+ void sortStartTimes();
+ void computeNestingLevels();
+ void computeNestingDepth();
+ void prepareForDisplay();
+ void linkStartsToEnds();
+ void linkEndsToStarts();
+ bool checkBindingLoop(QmlRangeEventData *from, QmlRangeEventData *current, QList<QmlRangeEventData *>visited);
+
+
+ // stats
+ void clearStatistics();
+ void redoTree(qint64 fromTime, qint64 toTime);
+ void computeMedianTime(qint64 fromTime, qint64 toTime);
+ void findBindingLoops(qint64 fromTime, qint64 toTime);
+
+ QmlProfilerDataModel::State listState;
+
+ // Stored data
+ QHash<QString, QmlRangeEventData *> rangeEventDictionary;
+ QVector<QmlRangeEventEndInstance> endInstanceList;
+ QVector<QmlRangeEventStartInstance> startInstanceList;
+
+ QmlRangeEventData qmlRootEvent;
+
+ QV8ProfilerDataModel *v8DataModel;
+
+ QHash<int, QmlRangeEventTypeCount *> typeCounts;
+
+ qint64 traceEndTime;
+ qint64 traceStartTime;
+ qint64 qmlMeasuredTime;
+
+ QmlRangeEventStartInstance *lastFrameEvent;
+ qint64 maxAnimationCount;
+ qint64 minAnimationCount;
+
+ // file to load
+ QString fileName;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+QmlProfilerDataModel::QmlProfilerDataModel(QObject *parent) :
+ QObject(parent), d(new QmlProfilerDataModelPrivate(this))
+{
+ setObjectName("QmlProfilerDataModel");
+
+ d->listState = Empty;
+
+ d->traceEndTime = 0;
+ d->traceStartTime = -1;
+ d->qmlMeasuredTime = 0;
+ d->clearQmlRootEvent();
+ d->lastFrameEvent = 0;
+ d->maxAnimationCount = 0;
+ d->minAnimationCount = 0;
+ d->v8DataModel = new QV8ProfilerDataModel(this, this);
+}
+
+QmlProfilerDataModel::~QmlProfilerDataModel()
+{
+ clear();
+ delete d;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+QList<QmlRangeEventData *> QmlProfilerDataModel::getEventDescriptions() const
+{
+ return d->rangeEventDictionary.values();
+}
+
+QmlRangeEventData *QmlProfilerDataModel::eventDescription(int eventId) const
+{
+ foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
+ if (event->eventId == eventId)
+ return event;
+ }
+ return 0;
+}
+
+QList<QV8EventData *> QmlProfilerDataModel::getV8Events() const
+{
+ return d->v8DataModel->getV8Events();
+}
+
+QV8EventData *QmlProfilerDataModel::v8EventDescription(int eventId) const
+{
+ return d->v8DataModel->v8EventDescription(eventId);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::clear()
+{
+ qDeleteAll(d->rangeEventDictionary.values());
+ d->rangeEventDictionary.clear();
+
+ d->endInstanceList.clear();
+ d->startInstanceList.clear();
+
+ d->clearQmlRootEvent();
+
+ foreach (QmlRangeEventTypeCount *typeCount, d->typeCounts.values())
+ delete typeCount;
+ d->typeCounts.clear();
+
+ d->traceEndTime = 0;
+ d->traceStartTime = -1;
+ d->qmlMeasuredTime = 0;
+
+ d->lastFrameEvent = 0;
+ d->maxAnimationCount = 0;
+ d->minAnimationCount = 0;
+
+ d->v8DataModel->clear();
+
+ emit countChanged();
+ setState(Empty);
+}
+
+void QmlProfilerDataModel::addRangedEvent(int type, qint64 startTime, qint64 length,
+ const QStringList &data,
+ const QmlJsDebugClient::QmlEventLocation &location)
+{
+ const QChar colon = QLatin1Char(':');
+ QString displayName, eventHashStr, details;
+ QmlJsDebugClient::QmlEventLocation eventLocation = location;
+
+ setState(AcquiringData);
+
+ // generate details string
+ if (data.isEmpty())
+ details = tr("Source code not available");
+ else {
+ details = data.join(" ").replace('\n'," ").simplified();
+ QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
+ bool match = rewrite.exactMatch(details);
+ if (match) {
+ details = rewrite.cap(1) + ": " + rewrite.cap(3);
+ }
+ if (details.startsWith(QString("file://")))
+ details = details.mid(details.lastIndexOf(QChar('/')) + 1);
+ }
+
+ // backwards compatibility: "compiling" events don't have a proper location in older
+ // version of the protocol, but the filename is passed in the details string
+ if (type == QmlJsDebugClient::Compiling && eventLocation.filename.isEmpty()) {
+ eventLocation.filename = details;
+ eventLocation.line = 1;
+ eventLocation.column = 1;
+ }
+
+ // generate hash
+ if (eventLocation.filename.isEmpty()) {
+ displayName = tr("<bytecode>");
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ } else {
+ const QString filePath = QUrl(eventLocation.filename).path();
+ displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon +
+ QString::number(eventLocation.line);
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ }
+
+ QmlRangeEventData *newEvent;
+ if (d->rangeEventDictionary.contains(eventHashStr)) {
+ newEvent = d->rangeEventDictionary[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData;
+ newEvent->displayName = displayName;
+ newEvent->location = eventLocation;
+ newEvent->eventHashStr = eventHashStr;
+ newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
+ newEvent->details = details;
+ d->rangeEventDictionary.insert(eventHashStr, newEvent);
+ }
+
+ QmlRangeEventEndInstance endTimeData;
+ endTimeData.endTime = startTime + length;
+ endTimeData.description = newEvent;
+ endTimeData.startTimeIndex = d->startInstanceList.count();
+
+ QmlRangeEventStartInstance startTimeData;
+ startTimeData.startTime = startTime;
+ startTimeData.duration = length;
+ startTimeData.statsInfo = newEvent;
+ startTimeData.endTimeIndex = d->endInstanceList.count();
+ startTimeData.animationCount = -1;
+ startTimeData.frameRate = 1e9/length;
+ startTimeData.baseEventIndex = d->startInstanceList.count(); // point to itself by default
+
+ d->endInstanceList << endTimeData;
+ d->startInstanceList << startTimeData;
+
+ emit countChanged();
+}
+
+void QmlProfilerDataModel::addV8Event(int depth, const QString &function,
+ const QString &filename, int lineNumber,
+ double totalTime, double selfTime)
+{
+ d->v8DataModel->addV8Event(depth, function, filename, lineNumber, totalTime, selfTime);
+}
+
+void QmlProfilerDataModel::addFrameEvent(qint64 time, int framerate, int animationcount)
+{
+ QString displayName, eventHashStr, details;
+
+ setState(AcquiringData);
+
+ details = tr("Animation Timer Update");
+ displayName = tr("<Animation Update>");
+ eventHashStr = displayName;
+
+ QmlRangeEventData *newEvent;
+ if (d->rangeEventDictionary.contains(eventHashStr)) {
+ newEvent = d->rangeEventDictionary[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData;
+ newEvent->displayName = displayName;
+ newEvent->eventHashStr = eventHashStr;
+ newEvent->eventType = QmlJsDebugClient::Painting;
+ newEvent->details = details;
+ d->rangeEventDictionary.insert(eventHashStr, newEvent);
+ }
+
+ qint64 length = 1e9/framerate;
+ // avoid overlap
+ if (d->lastFrameEvent &&
+ d->lastFrameEvent->startTime + d->lastFrameEvent->duration >= time) {
+ d->lastFrameEvent->duration = time - 1 - d->lastFrameEvent->startTime;
+ d->endInstanceList[d->lastFrameEvent->endTimeIndex].endTime =
+ d->lastFrameEvent->startTime + d->lastFrameEvent->duration;
+ }
+
+ QmlRangeEventEndInstance endTimeData;
+ endTimeData.endTime = time + length;
+ endTimeData.description = newEvent;
+ endTimeData.startTimeIndex = d->startInstanceList.count();
+
+ QmlRangeEventStartInstance startTimeData;
+ startTimeData.startTime = time;
+ startTimeData.duration = length;
+ startTimeData.statsInfo = newEvent;
+ startTimeData.endTimeIndex = d->endInstanceList.count();
+ startTimeData.animationCount = animationcount;
+ startTimeData.frameRate = framerate;
+ startTimeData.baseEventIndex = d->startInstanceList.count(); // point to itself by default
+
+ d->endInstanceList << endTimeData;
+ d->startInstanceList << startTimeData;
+
+ d->lastFrameEvent = &d->startInstanceList.last();
+
+ emit countChanged();
+}
+
+void QmlProfilerDataModel::setTraceEndTime(qint64 time)
+{
+ d->traceEndTime = time;
+}
+
+void QmlProfilerDataModel::setTraceStartTime(qint64 time)
+{
+ d->traceStartTime = time;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QString QmlProfilerDataModel::getHashStringForQmlEvent(
+ const QmlJsDebugClient::QmlEventLocation &location, int eventType)
+{
+ return QString("%1:%2:%3:%4").arg(location.filename,
+ QString::number(location.line),
+ QString::number(location.column),
+ QString::number(eventType));
+}
+
+QString QmlProfilerDataModel::getHashStringForV8Event(const QString &displayName,
+ const QString &function)
+{
+ return QString("%1:%2").arg(displayName, function);
+}
+
+QString QmlProfilerDataModel::rootEventName()
+{
+ return tr("<program>");
+}
+
+QString QmlProfilerDataModel::rootEventDescription()
+{
+ return tr("Main Program");
+}
+
+QString QmlProfilerDataModel::qmlEventTypeAsString(QmlEventType typeEnum)
+{
+ switch (typeEnum) {
+ case Painting:
+ return QLatin1String(Constants::TYPE_PAINTING_STR);
+ break;
+ case Compiling:
+ return QLatin1String(Constants::TYPE_COMPILING_STR);
+ break;
+ case Creating:
+ return QLatin1String(Constants::TYPE_CREATING_STR);
+ break;
+ case Binding:
+ return QLatin1String(Constants::TYPE_BINDING_STR);
+ break;
+ case HandlingSignal:
+ return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+QmlEventType QmlProfilerDataModel::qmlEventTypeAsEnum(const QString &typeString)
+{
+ if (typeString == QLatin1String(Constants::TYPE_PAINTING_STR)) {
+ return Painting;
+ } else if (typeString == QLatin1String(Constants::TYPE_COMPILING_STR)) {
+ return Compiling;
+ } else if (typeString == QLatin1String(Constants::TYPE_CREATING_STR)) {
+ return Creating;
+ } else if (typeString == QLatin1String(Constants::TYPE_BINDING_STR)) {
+ return Binding;
+ } else if (typeString == QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR)) {
+ return HandlingSignal;
+ } else {
+ bool isNumber = false;
+ int type = typeString.toUInt(&isNumber);
+ if (isNumber) {
+ return (QmlEventType)type;
+ } else {
+ return MaximumQmlEventType;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+int QmlProfilerDataModel::findFirstIndex(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endInstanceList.isEmpty())
+ return 0; // -1
+ if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endInstanceList.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1)
+ {
+ int fromIndex = 0;
+ int toIndex = d->endInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endInstanceList[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int eventIndex = d->endInstanceList[candidate].startTimeIndex;
+ return d->startInstanceList[eventIndex].baseEventIndex;
+}
+
+int QmlProfilerDataModel::findFirstIndexNoParents(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endInstanceList.isEmpty())
+ return 0; // -1
+ if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endInstanceList.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1) {
+ int fromIndex = 0;
+ int toIndex = d->endInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endInstanceList[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->endInstanceList[candidate].startTimeIndex;
+
+ return ndx;
+}
+
+int QmlProfilerDataModel::findLastIndex(qint64 endTime) const
+{
+ // in the "starttime" list, find the last event that starts before endtime
+ if (d->startInstanceList.isEmpty())
+ return 0; // -1
+ if (d->startInstanceList.first().startTime >= endTime)
+ return 0; // -1
+ if (d->startInstanceList.count() == 1)
+ return 0;
+ if (d->startInstanceList.last().startTime <= endTime)
+ return d->startInstanceList.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->startInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->startInstanceList[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+qint64 QmlProfilerDataModel::firstTimeMark() const
+{
+ if (d->startInstanceList.isEmpty())
+ return 0;
+ else {
+ return d->startInstanceList[0].startTime;
+ }
+}
+
+qint64 QmlProfilerDataModel::lastTimeMark() const
+{
+ if (d->endInstanceList.isEmpty())
+ return 0;
+ else {
+ return d->endInstanceList.last().endTime;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+int QmlProfilerDataModel::count() const
+{
+ return d->startInstanceList.count();
+}
+
+bool QmlProfilerDataModel::isEmpty() const
+{
+ return d->startInstanceList.isEmpty() && d->v8DataModel->isEmpty();
+}
+
+qint64 QmlProfilerDataModel::getStartTime(int index) const
+{
+ return d->startInstanceList[index].startTime;
+}
+
+qint64 QmlProfilerDataModel::getEndTime(int index) const
+{
+ return d->startInstanceList[index].startTime + d->startInstanceList[index].duration;
+}
+
+qint64 QmlProfilerDataModel::getDuration(int index) const
+{
+ return d->startInstanceList[index].duration;
+}
+
+int QmlProfilerDataModel::getType(int index) const
+{
+ return d->startInstanceList[index].statsInfo->eventType;
+}
+
+int QmlProfilerDataModel::getNestingLevel(int index) const
+{
+ return d->startInstanceList[index].nestingLevel;
+}
+
+int QmlProfilerDataModel::getNestingDepth(int index) const
+{
+ return d->startInstanceList[index].nestingDepth;
+}
+
+QString QmlProfilerDataModel::getFilename(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.filename;
+}
+
+int QmlProfilerDataModel::getLine(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.line;
+}
+
+int QmlProfilerDataModel::getColumn(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.column;
+}
+
+QString QmlProfilerDataModel::getDetails(int index) const
+{
+ // special: animations
+ if (d->startInstanceList[index].statsInfo->eventType == QmlJsDebugClient::Painting &&
+ d->startInstanceList[index].animationCount >= 0)
+ return tr("%1 animations at %2 FPS").arg(
+ QString::number(d->startInstanceList[index].animationCount),
+ QString::number(d->startInstanceList[index].frameRate));
+ return d->startInstanceList[index].statsInfo->details;
+}
+
+int QmlProfilerDataModel::getEventId(int index) const
+{
+ return d->startInstanceList[index].statsInfo->eventId;
+}
+
+int QmlProfilerDataModel::getBindingLoopDest(int index) const
+{
+ return d->startInstanceList[index].bindingLoopHead;
+}
+
+int QmlProfilerDataModel::getFramerate(int index) const
+{
+ return d->startInstanceList[index].frameRate;
+}
+
+int QmlProfilerDataModel::getAnimationCount(int index) const
+{
+ return d->startInstanceList[index].animationCount;
+}
+
+int QmlProfilerDataModel::getMaximumAnimationCount() const
+{
+ return d->maxAnimationCount;
+}
+
+int QmlProfilerDataModel::getMinimumAnimationCount() const
+{
+ return d->minAnimationCount;
+}
+
+/////////////////////////////////////////
+
+int QmlProfilerDataModel::uniqueEventsOfType(int type) const
+{
+ if (!d->typeCounts.contains(type))
+ return 0;
+ return d->typeCounts[type]->eventIds.count();
+}
+
+int QmlProfilerDataModel::maxNestingForType(int type) const
+{
+ if (!d->typeCounts.contains(type))
+ return 0;
+ return d->typeCounts[type]->nestingCount;
+}
+
+QString QmlProfilerDataModel::eventTextForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return QString();
+ return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->details;
+}
+
+QString QmlProfilerDataModel::eventDisplayNameForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return QString();
+ return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->displayName;
+}
+
+int QmlProfilerDataModel::eventIdForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return -1;
+ return d->typeCounts[type]->eventIds[index];
+}
+
+int QmlProfilerDataModel::eventPosInType(int index) const
+{
+ int eventType = d->startInstanceList[index].statsInfo->eventType;
+ return d->typeCounts[eventType]->eventIds.indexOf(d->startInstanceList[index].statsInfo->eventId);
+}
+
+/////////////////////////////////////////
+
+qint64 QmlProfilerDataModel::traceStartTime() const
+{
+ return d->traceStartTime != -1? d->traceStartTime : firstTimeMark();
+}
+
+qint64 QmlProfilerDataModel::traceEndTime() const
+{
+ return d->traceEndTime ? d->traceEndTime : lastTimeMark();
+}
+
+qint64 QmlProfilerDataModel::traceDuration() const
+{
+ return traceEndTime() - traceStartTime();
+}
+
+qint64 QmlProfilerDataModel::qmlMeasuredTime() const
+{
+ return d->qmlMeasuredTime;
+}
+qint64 QmlProfilerDataModel::v8MeasuredTime() const
+{
+ return d->v8DataModel->v8MeasuredTime();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::complete()
+{
+ if (currentState() == AcquiringData) {
+ setState(ProcessingData);
+ d->v8DataModel->collectV8Statistics();
+ d->postProcess();
+ } else
+ if (currentState() == Empty) {
+ setState(Done);
+ } else {
+ emit error("Unexpected complete signal in data model");
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::postProcess()
+{
+ if (q->count() != 0) {
+ sortStartTimes();
+ sortEndTimes();
+ findAnimationLimits();
+ computeNestingLevels();
+ computeNestingDepth();
+ linkEndsToStarts();
+ insertQmlRootEvent();
+ q->reloadDetails();
+ prepareForDisplay();
+ q->compileStatistics(q->traceStartTime(), q->traceEndTime());
+
+ }
+ q->setState(Done);
+}
+
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::prepareForDisplay()
+{
+ // generate numeric ids
+ int ndx = 0;
+ foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
+ binding->eventId = ndx++;
+ }
+
+ // collect type counts
+ foreach (const QmlRangeEventStartInstance &eventStartData, startInstanceList) {
+ int typeNumber = eventStartData.statsInfo->eventType;
+ if (!typeCounts.contains(typeNumber)) {
+ typeCounts[typeNumber] = new QmlRangeEventTypeCount;
+ typeCounts[typeNumber]->nestingCount = 0;
+ }
+ if (eventStartData.nestingLevel > typeCounts[typeNumber]->nestingCount) {
+ typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
+ }
+ if (!typeCounts[typeNumber]->eventIds.contains(eventStartData.statsInfo->eventId))
+ typeCounts[typeNumber]->eventIds << eventStartData.statsInfo->eventId;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortStartTimes()
+{
+ if (startInstanceList.count() < 2)
+ return;
+
+ // assuming startTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+ QVector<QmlRangeEventStartInstance>::iterator itFrom = startInstanceList.end() - 2;
+ QVector<QmlRangeEventStartInstance>::iterator itTo = startInstanceList.end() - 1;
+
+ while (itFrom != startInstanceList.begin() && itTo != startInstanceList.begin()) {
+ // find block to sort
+ while (itFrom != startInstanceList.begin()
+ && itTo->startTime > itFrom->startTime) {
+ itTo--;
+ itFrom = itTo - 1;
+ }
+
+ // if we're at the end of the list
+ if (itFrom == startInstanceList.begin())
+ break;
+
+ // find block length
+ while (itFrom != startInstanceList.begin()
+ && itTo->startTime <= itFrom->startTime)
+ itFrom--;
+
+ if (itTo->startTime <= itFrom->startTime)
+ qSort(itFrom, itTo + 1, compareStartTimes);
+ else
+ qSort(itFrom + 1, itTo + 1, compareStartTimes);
+
+ // move to next block
+ itTo = itFrom;
+ itFrom = itTo - 1;
+ }
+
+ // link back the endTimes
+ linkEndsToStarts();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortEndTimes()
+{
+ // assuming endTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+
+ if (endInstanceList.count() < 2)
+ return;
+
+ QVector<QmlRangeEventEndInstance>::iterator itFrom = endInstanceList.begin();
+ QVector<QmlRangeEventEndInstance>::iterator itTo = endInstanceList.begin() + 1;
+
+ while (itTo != endInstanceList.end() && itFrom != endInstanceList.end()) {
+ // find block to sort
+ while (itTo != endInstanceList.end()
+ && startInstanceList[itTo->startTimeIndex].startTime >
+ startInstanceList[itFrom->startTimeIndex].startTime +
+ startInstanceList[itFrom->startTimeIndex].duration) {
+ itFrom++;
+ itTo = itFrom+1;
+ }
+
+ // if we're at the end of the list
+ if (itTo == endInstanceList.end())
+ break;
+
+ // find block length
+ while (itTo != endInstanceList.end()
+ && startInstanceList[itTo->startTimeIndex].startTime <=
+ startInstanceList[itFrom->startTimeIndex].startTime +
+ startInstanceList[itFrom->startTimeIndex].duration)
+ itTo++;
+
+ // sort block
+ qSort(itFrom, itTo, compareEndTimes);
+
+ // move to next block
+ itFrom = itTo;
+ itTo = itFrom+1;
+
+ }
+
+ // link back the startTimes
+ linkStartsToEnds();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkStartsToEnds()
+{
+ for (int i = 0; i < endInstanceList.count(); i++)
+ startInstanceList[endInstanceList[i].startTimeIndex].endTimeIndex = i;
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findAnimationLimits()
+{
+ maxAnimationCount = 0;
+ minAnimationCount = 0;
+ lastFrameEvent = 0;
+
+ for (int i = 0; i < startInstanceList.count(); i++) {
+ if (startInstanceList[i].statsInfo->eventType == QmlJsDebugClient::Painting &&
+ startInstanceList[i].animationCount >= 0) {
+ int animationcount = startInstanceList[i].animationCount;
+ if (lastFrameEvent) {
+ if (animationcount > maxAnimationCount)
+ maxAnimationCount = animationcount;
+ if (animationcount < minAnimationCount)
+ minAnimationCount = animationcount;
+ } else {
+ maxAnimationCount = animationcount;
+ minAnimationCount = animationcount;
+ }
+ lastFrameEvent = &startInstanceList[i];
+ }
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingLevels()
+{
+ // compute levels
+ QHash<int, qint64> endtimesPerLevel;
+ QList<int> nestingLevels;
+ QList< QHash<int, qint64> > endtimesPerNestingLevel;
+ int level = Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[Constants::QML_MIN_LEVEL] = 0;
+ int lastBaseEventIndex = 0;
+ qint64 lastBaseEventEndTime = traceStartTime;
+
+ for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
+ nestingLevels << Constants::QML_MIN_LEVEL;
+ QHash<int, qint64> dummyHash;
+ dummyHash[Constants::QML_MIN_LEVEL] = 0;
+ endtimesPerNestingLevel << dummyHash;
+ }
+
+ for (int i=0; i<startInstanceList.count(); i++) {
+ qint64 st = startInstanceList[i].startTime;
+ int type = startInstanceList[i].statsInfo->eventType;
+
+ if (type == QmlJsDebugClient::Painting) {
+ // animation/paint events have level 0 by definition (same as "mainprogram"),
+ // but are not considered parents of other events for statistical purposes
+ startInstanceList[i].level = Constants::QML_MIN_LEVEL - 1;
+ startInstanceList[i].nestingLevel = Constants::QML_MIN_LEVEL;
+ if (lastBaseEventEndTime < startInstanceList[i].startTime) {
+ lastBaseEventIndex = i;
+ lastBaseEventEndTime = startInstanceList[i].startTime + startInstanceList[i].duration;
+ }
+ startInstanceList[i].baseEventIndex = lastBaseEventIndex;
+ continue;
+ }
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + startInstanceList[i].duration;
+
+ // per type
+ if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+ nestingLevels[type]++;
+ } else {
+ while (nestingLevels[type] > Constants::QML_MIN_LEVEL &&
+ endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+ nestingLevels[type]--;
+ }
+ endtimesPerNestingLevel[type][nestingLevels[type]] =
+ st + startInstanceList[i].duration;
+
+ startInstanceList[i].level = level;
+ startInstanceList[i].nestingLevel = nestingLevels[type];
+
+ if (level == Constants::QML_MIN_LEVEL) {
+ qmlMeasuredTime += startInstanceList[i].duration;
+ if (lastBaseEventEndTime < startInstanceList[i].startTime) {
+ lastBaseEventIndex = i;
+ lastBaseEventEndTime = startInstanceList[i].startTime + startInstanceList[i].duration;
+ }
+ }
+ startInstanceList[i].baseEventIndex = lastBaseEventIndex;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingDepth()
+{
+ QHash<int, int> nestingDepth;
+ for (int i = 0; i < endInstanceList.count(); i++) {
+ int type = endInstanceList[i].description->eventType;
+ int nestingInType = startInstanceList[endInstanceList[i].startTimeIndex].nestingLevel;
+ if (!nestingDepth.contains(type))
+ nestingDepth[type] = nestingInType;
+ else {
+ int nd = nestingDepth[type];
+ nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
+ }
+
+ startInstanceList[endInstanceList[i].startTimeIndex].nestingDepth = nestingDepth[type];
+ if (nestingInType == Constants::QML_MIN_LEVEL)
+ nestingDepth[type] = Constants::QML_MIN_LEVEL;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkEndsToStarts()
+{
+ for (int i = 0; i < startInstanceList.count(); i++)
+ endInstanceList[startInstanceList[i].endTimeIndex].startTimeIndex = i;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::compileStatistics(qint64 startTime, qint64 endTime)
+{
+ d->clearStatistics();
+ d->redoTree(startTime, endTime);
+ d->computeMedianTime(startTime, endTime);
+ d->findBindingLoops(startTime, endTime);
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearStatistics()
+{
+ // clear existing statistics
+ foreach (QmlRangeEventData *eventDescription, rangeEventDictionary.values()) {
+ eventDescription->calls = 0;
+ // maximum possible value
+ eventDescription->minTime = endInstanceList.last().endTime;
+ eventDescription->maxTime = 0;
+ eventDescription->medianTime = 0;
+ eventDescription->duration = 0;
+ qDeleteAll(eventDescription->parentHash);
+ qDeleteAll(eventDescription->childrenHash);
+ eventDescription->parentHash.clear();
+ eventDescription->childrenHash.clear();
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::redoTree(qint64 fromTime,
+ qint64 toTime)
+{
+ double totalTime = 0;
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
+ QTC_ASSERT(listedRootEvent, /**/);
+
+ // compute parent-child relationship and call count
+ QHash<int, QmlRangeEventData*> lastParent;
+ for (int index = fromIndex; index <= toIndex; index++) {
+ QmlRangeEventData *eventDescription = startInstanceList[index].statsInfo;
+
+ if (startInstanceList[index].startTime > toTime ||
+ startInstanceList[index].startTime+startInstanceList[index].duration < fromTime) {
+ continue;
+ }
+
+ if (eventDescription->eventType == QmlJsDebugClient::Painting) {
+ // skip animation/paint events
+ continue;
+ }
+
+ eventDescription->calls++;
+ qint64 duration = startInstanceList[index].duration;
+ eventDescription->duration += duration;
+ if (eventDescription->maxTime < duration)
+ eventDescription->maxTime = duration;
+ if (eventDescription->minTime > duration)
+ eventDescription->minTime = duration;
+
+ int level = startInstanceList[index].level;
+
+ QmlRangeEventData *parentEvent = listedRootEvent;
+ if (level > Constants::QML_MIN_LEVEL && lastParent.contains(level-1)) {
+ parentEvent = lastParent[level-1];
+ }
+
+ if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
+ QmlRangeEventRelative *newParentEvent = new QmlRangeEventRelative(parentEvent);
+ newParentEvent->calls = 1;
+ newParentEvent->duration = duration;
+
+ eventDescription->parentHash.insert(parentEvent->eventHashStr, newParentEvent);
+ } else {
+ QmlRangeEventRelative *newParentEvent =
+ eventDescription->parentHash.value(parentEvent->eventHashStr);
+ newParentEvent->duration += duration;
+ newParentEvent->calls++;
+ }
+
+ if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
+ QmlRangeEventRelative *newChildEvent = new QmlRangeEventRelative(eventDescription);
+ newChildEvent->calls = 1;
+ newChildEvent->duration = duration;
+
+ parentEvent->childrenHash.insert(eventDescription->eventHashStr, newChildEvent);
+ } else {
+ QmlRangeEventRelative *newChildEvent =
+ parentEvent->childrenHash.value(eventDescription->eventHashStr);
+ newChildEvent->duration += duration;
+ newChildEvent->calls++;
+ }
+
+ lastParent[level] = eventDescription;
+
+ if (level == Constants::QML_MIN_LEVEL) {
+ totalTime += duration;
+ }
+ }
+
+ // fake rootEvent statistics
+ // the +1 nanosecond is to force it to be on top of the sorted list
+ listedRootEvent->duration = totalTime+1;
+ listedRootEvent->minTime = totalTime+1;
+ listedRootEvent->maxTime = totalTime+1;
+ listedRootEvent->medianTime = totalTime+1;
+ if (totalTime > 0)
+ listedRootEvent->calls = 1;
+
+ // copy to the global root reference
+ qmlRootEvent = *listedRootEvent;
+
+ // compute percentages
+ foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
+ binding->percentOfTime = binding->duration * 100.0 / totalTime;
+ binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeMedianTime(qint64 fromTime,
+ qint64 toTime)
+{
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ // compute median time
+ QHash< QmlRangeEventData* , QList<qint64> > durationLists;
+ for (int index = fromIndex; index <= toIndex; index++) {
+ QmlRangeEventData *desc = startInstanceList[index].statsInfo;
+ qint64 len = startInstanceList[index].duration;
+ durationLists[desc].append(len);
+ }
+ QMutableHashIterator < QmlRangeEventData* , QList<qint64> > iter(durationLists);
+ while (iter.hasNext()) {
+ iter.next();
+ if (!iter.value().isEmpty()) {
+ qSort(iter.value());
+ iter.key()->medianTime = iter.value().at(iter.value().count()/2);
+ }
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findBindingLoops(qint64 fromTime,
+ qint64 toTime)
+{
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ // first clear existing data
+ foreach (QmlRangeEventData *event, rangeEventDictionary.values()) {
+ event->isBindingLoop = false;
+ foreach (QmlRangeEventRelative *parentEvent, event->parentHash.values())
+ parentEvent->inLoopPath = false;
+ foreach (QmlRangeEventRelative *childEvent, event->childrenHash.values())
+ childEvent->inLoopPath = false;
+ }
+
+ QList<QmlRangeEventData *> stackRefs;
+ QList<QmlRangeEventStartInstance *> stack;
+
+ for (int i = 0; i < startInstanceList.count(); i++) {
+ QmlRangeEventData *currentEvent = startInstanceList[i].statsInfo;
+ QmlRangeEventStartInstance *inTimeEvent = &startInstanceList[i];
+ inTimeEvent->bindingLoopHead = -1;
+
+ // special: skip animation events (but not old paint events)
+ if (inTimeEvent->statsInfo->eventType == Painting && inTimeEvent->animationCount >= 0)
+ continue;
+
+ // managing call stack
+ for (int j = stack.count() - 1; j >= 0; j--) {
+ if (stack[j]->startTime + stack[j]->duration <= inTimeEvent->startTime) {
+ stack.removeAt(j);
+ stackRefs.removeAt(j);
+ }
+ }
+
+ bool loopDetected = stackRefs.contains(currentEvent);
+ stack << inTimeEvent;
+ stackRefs << currentEvent;
+
+ if (loopDetected) {
+ if (i >= fromIndex && i <= toIndex) {
+ // for the statistics
+ currentEvent->isBindingLoop = true;
+ for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
+ QmlRangeEventRelative *nextEventSub =
+ stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
+ nextEventSub->inLoopPath = true;
+ QmlRangeEventRelative *prevEventSub =
+ stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
+ prevEventSub->inLoopPath = true;
+ }
+ }
+
+ // use crossed references to find index in starttimesortedlist
+ QmlRangeEventStartInstance *head = stack[stackRefs.indexOf(currentEvent)];
+ inTimeEvent->bindingLoopHead = endInstanceList[head->endTimeIndex].startTimeIndex;
+ startInstanceList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearQmlRootEvent()
+{
+ qmlRootEvent.displayName = rootEventName();
+ qmlRootEvent.location = QmlEventLocation();
+ qmlRootEvent.eventHashStr = rootEventName();
+ qmlRootEvent.details = rootEventDescription();
+ qmlRootEvent.eventType = QmlJsDebugClient::Binding;
+ qmlRootEvent.duration = 0;
+ qmlRootEvent.calls = 0;
+ qmlRootEvent.minTime = 0;
+ qmlRootEvent.maxTime = 0;
+ qmlRootEvent.timePerCall = 0;
+ qmlRootEvent.percentOfTime = 0;
+ qmlRootEvent.medianTime = 0;
+ qmlRootEvent.eventId = -1;
+
+ qDeleteAll(qmlRootEvent.parentHash.values());
+ qDeleteAll(qmlRootEvent.childrenHash.values());
+ qmlRootEvent.parentHash.clear();
+ qmlRootEvent.childrenHash.clear();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::insertQmlRootEvent()
+{
+ // create root event for statistics & insert into list
+ clearQmlRootEvent();
+ QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
+ if (!listedRootEvent) {
+ listedRootEvent = new QmlRangeEventData;
+ rangeEventDictionary.insert(rootEventName(), listedRootEvent);
+ }
+ *listedRootEvent = qmlRootEvent;
+}
+
+void QmlProfilerDataModel::reloadDetails()
+{
+ // request binding/signal details from the AST
+ foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
+ if (event->eventType != Binding && event->eventType != HandlingSignal)
+ continue;
+
+ // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
+ if (event->location.filename.isEmpty())
+ continue;
+
+ // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
+ if (event->location.column == -1)
+ continue;
+
+ emit requestDetailsForLocation(event->eventType, event->location);
+ }
+ emit reloadDocumentsForDetails();
+}
+
+void QmlProfilerDataModel::rewriteDetailsString(int eventType,
+ const QmlJsDebugClient::QmlEventLocation &location,
+ const QString &newString)
+{
+ QString eventHashStr = getHashStringForQmlEvent(location, eventType);
+ QTC_ASSERT(d->rangeEventDictionary.contains(eventHashStr), return);
+ d->rangeEventDictionary.value(eventHashStr)->details = newString;
+ emit detailsChanged(d->rangeEventDictionary.value(eventHashStr)->eventId, newString);
+}
+
+void QmlProfilerDataModel::finishedRewritingDetails()
+{
+ emit reloadDetailLabels();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+bool QmlProfilerDataModel::save(const QString &filename)
+{
+ if (isEmpty()) {
+ emit error(tr("No data to save"));
+ return false;
+ }
+
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing").arg(filename));
+ return false;
+ }
+
+ QXmlStreamWriter stream(&file);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement("trace");
+ stream.writeAttribute("version", Constants::PROFILER_FILE_VERSION);
+
+ stream.writeAttribute("traceStart", QString::number(traceStartTime()));
+ stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
+
+ stream.writeStartElement("eventData");
+ stream.writeAttribute("totalTime", QString::number(d->qmlMeasuredTime));
+
+ foreach (const QmlRangeEventData *eventData, d->rangeEventDictionary.values()) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index", QString::number(d->rangeEventDictionary.keys().indexOf(eventData->eventHashStr)));
+ stream.writeTextElement("displayname", eventData->displayName);
+ stream.writeTextElement("type", qmlEventTypeAsString(eventData->eventType));
+ if (!eventData->location.filename.isEmpty()) {
+ stream.writeTextElement("filename", eventData->location.filename);
+ stream.writeTextElement("line", QString::number(eventData->location.line));
+ stream.writeTextElement("column", QString::number(eventData->location.column));
+ }
+ stream.writeTextElement("details", eventData->details);
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement("profilerDataModel");
+ foreach (const QmlRangeEventStartInstance &rangedEvent, d->startInstanceList) {
+ stream.writeStartElement("range");
+ stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
+ stream.writeAttribute("duration", QString::number(rangedEvent.duration));
+ stream.writeAttribute("eventIndex", QString::number(d->rangeEventDictionary.keys().indexOf(rangedEvent.statsInfo->eventHashStr)));
+ if (rangedEvent.statsInfo->eventType == QmlJsDebugClient::Painting && rangedEvent.animationCount >= 0) {
+ // animation frame
+ stream.writeAttribute("framerate", QString::number(rangedEvent.frameRate));
+ stream.writeAttribute("animationcount", QString::number(rangedEvent.animationCount));
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ d->v8DataModel->save(stream);
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+
+ file.close();
+ return true;
+}
+
+void QmlProfilerDataModel::setFilename(const QString &filename)
+{
+ d->fileName = filename;
+}
+
+void QmlProfilerDataModel::load(const QString &filename)
+{
+ setFilename(filename);
+ load();
+}
+
+// "be strict in your output but tolerant in your inputs"
+void QmlProfilerDataModel::load()
+{
+ QString filename = d->fileName;
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ emit error(tr("Could not open %1 for reading").arg(filename));
+ return;
+ }
+
+ // erase current
+ clear();
+
+ setState(AcquiringData);
+
+ bool readingQmlEvents = false;
+ QHash<int, QmlRangeEventData *> descriptionBuffer;
+ QmlRangeEventData *currentEvent = 0;
+ bool startTimesAreSorted = true;
+ bool validVersion = true;
+
+ // time computation
+ d->qmlMeasuredTime = 0;
+
+ QXmlStreamReader stream(&file);
+
+ while (validVersion && !stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ QString elementName = stream.name().toString();
+ switch (token) {
+ case QXmlStreamReader::StartDocument : continue;
+ case QXmlStreamReader::StartElement : {
+ if (elementName == "trace") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("version"))
+ validVersion = attributes.value("version").toString() == Constants::PROFILER_FILE_VERSION;
+ else
+ validVersion = false;
+ if (attributes.hasAttribute("traceStart"))
+ setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
+ if (attributes.hasAttribute("traceEnd"))
+ setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
+ }
+ if (elementName == "eventData") {
+ readingQmlEvents = true;
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("totalTime"))
+ d->qmlMeasuredTime = attributes.value("totalTime").toString().toDouble();
+ break;
+ }
+ if (elementName == "v8profile" && !readingQmlEvents) {
+ d->v8DataModel->load(stream);
+ break;
+ }
+
+ if (elementName == "trace") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("traceStart"))
+ setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
+ if (attributes.hasAttribute("traceEnd"))
+ setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
+ }
+
+ if (elementName == "range") {
+ QmlRangeEventStartInstance rangedEvent;
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("startTime"))
+ rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
+ if (attributes.hasAttribute("duration"))
+ rangedEvent.duration = attributes.value("duration").toString().toLongLong();
+ if (attributes.hasAttribute("framerate"))
+ rangedEvent.frameRate = attributes.value("framerate").toString().toInt();
+ if (attributes.hasAttribute("animationcount"))
+ rangedEvent.animationCount = attributes.value("animationcount").toString().toInt();
+ else
+ rangedEvent.animationCount = -1;
+ if (attributes.hasAttribute("eventIndex")) {
+ int ndx = attributes.value("eventIndex").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlRangeEventData;
+ rangedEvent.statsInfo = descriptionBuffer.value(ndx);
+ }
+ rangedEvent.endTimeIndex = d->endInstanceList.count();
+
+ if (!d->startInstanceList.isEmpty()
+ && rangedEvent.startTime < d->startInstanceList.last().startTime)
+ startTimesAreSorted = false;
+ d->startInstanceList << rangedEvent;
+
+ QmlRangeEventEndInstance endTimeEvent;
+ endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.duration;
+ endTimeEvent.startTimeIndex = d->startInstanceList.count()-1;
+ endTimeEvent.description = rangedEvent.statsInfo;
+ d->endInstanceList << endTimeEvent;
+ break;
+ }
+
+ if (readingQmlEvents) {
+ if (elementName == "event") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("index")) {
+ int ndx = attributes.value("index").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlRangeEventData;
+ currentEvent = descriptionBuffer[ndx];
+ } else {
+ currentEvent = 0;
+ }
+ break;
+ }
+
+ // the remaining are eventdata or v8eventdata elements
+ if (!currentEvent)
+ break;
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+ QString readData = stream.text().toString();
+
+ if (elementName == "displayname") {
+ currentEvent->displayName = readData;
+ break;
+ }
+ if (elementName == "type") {
+ currentEvent->eventType = qmlEventTypeAsEnum(readData);
+ break;
+ }
+ if (elementName == "filename") {
+ currentEvent->location.filename = readData;
+ break;
+ }
+ if (elementName == "line") {
+ currentEvent->location.line = readData.toInt();
+ break;
+ }
+ if (elementName == "column") {
+ currentEvent->location.column = readData.toInt();
+ }
+ if (elementName == "details") {
+ currentEvent->details = readData;
+ break;
+ }
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "event") {
+ currentEvent = 0;
+ break;
+ }
+ if (elementName == "eventData") {
+ readingQmlEvents = false;
+ break;
+ }
+ }
+ default: break;
+ }
+ }
+
+ file.close();
+
+ if (stream.hasError()) {
+ emit error(tr("Error while parsing %1").arg(filename));
+ clear();
+ return;
+ }
+
+ stream.clear();
+
+ if (!validVersion) {
+ clear();
+ emit error(tr("Invalid version of QML Trace file."));
+ return;
+ }
+
+ // move the buffered data to the details cache
+ foreach (QmlRangeEventData *desc, descriptionBuffer.values()) {
+ desc->eventHashStr = getHashStringForQmlEvent(desc->location, desc->eventType);;
+ d->rangeEventDictionary[desc->eventHashStr] = desc;
+ }
+
+ // sort startTimeSortedList
+ if (!startTimesAreSorted) {
+ qSort(d->startInstanceList.begin(), d->startInstanceList.end(), compareStartTimes);
+ for (int i = 0; i< d->startInstanceList.count(); i++) {
+ QmlRangeEventStartInstance startTimeData = d->startInstanceList[i];
+ d->endInstanceList[startTimeData.endTimeIndex].startTimeIndex = i;
+ }
+ qSort(d->endInstanceList.begin(), d->endInstanceList.end(), compareStartIndexes);
+ }
+
+ emit countChanged();
+
+ descriptionBuffer.clear();
+
+ setState(ProcessingData);
+ d->postProcess();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerDataModel::State QmlProfilerDataModel::currentState() const
+{
+ return d->listState;
+}
+
+int QmlProfilerDataModel::getCurrentStateFromQml() const
+{
+ return (int)d->listState;
+}
+
+void QmlProfilerDataModel::setState(QmlProfilerDataModel::State state)
+{
+ // It's not an error, we are continuously calling "AcquiringData" for example
+ if (d->listState == state)
+ return;
+
+ switch (state) {
+ case Empty:
+ // if it's not empty, complain but go on
+ QTC_ASSERT(isEmpty(), /**/);
+ break;
+ case AcquiringData:
+ // we're not supposed to receive new data while processing older data
+ QTC_ASSERT(d->listState != ProcessingData, return);
+ break;
+ case ProcessingData:
+ QTC_ASSERT(d->listState == AcquiringData, return);
+ break;
+ case Done:
+ QTC_ASSERT(d->listState == ProcessingData || d->listState == Empty, return);
+ break;
+ default:
+ emit error("Trying to set unknown state in events list");
+ break;
+ }
+
+ d->listState = state;
+ emit stateChanged();
+
+ // special: if we were done with an empty list, clean internal data and go back to empty
+ if (d->listState == Done && isEmpty()) {
+ clear();
+ }
+ return;
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventStartInstance, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventEndInstance, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventRelative, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
index 32f5203c2a..7e2ab03470 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
@@ -30,33 +30,37 @@
**
**************************************************************************/
-#ifndef QMLPROFILEREVENTLIST_H
-#define QMLPROFILEREVENTLIST_H
+#ifndef QMLPROFILERDATAMODEL_H
+#define QMLPROFILERDATAMODEL_H
-#include "qmlprofilereventtypes.h"
-#include "qmlprofilereventlocation.h"
-#include "qmljsdebugclient_global.h"
+#include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
+#include "qv8profilerdatamodel.h"
#include <QHash>
#include <QObject>
-namespace QmlJsDebugClient {
+namespace QmlProfiler {
+namespace Internal {
-struct QmlEventSub;
-struct QV8EventSub;
+// used for parents and children
+struct QmlRangeEventRelative;
-struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
+struct QmlRangeEventData
{
- QmlEventData();
- ~QmlEventData();
+ QmlRangeEventData();
+ ~QmlRangeEventData();
- QString displayname;
+ int eventId;
+ QString displayName;
QString eventHashStr;
QString details;
- QmlEventLocation location;
+ QmlJsDebugClient::QmlEventLocation location;
QmlJsDebugClient::QmlEventType eventType;
- QHash <QString, QmlEventSub *> parentHash;
- QHash <QString, QmlEventSub *> childrenHash;
+
+ QHash <QString, QmlRangeEventRelative *> parentHash;
+ QHash <QString, QmlRangeEventRelative *> childrenHash;
+
qint64 duration;
qint64 calls;
qint64 minTime;
@@ -64,54 +68,22 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
double timePerCall;
double percentOfTime;
qint64 medianTime;
- int eventId;
+
bool isBindingLoop;
- QmlEventData &operator=(const QmlEventData &ref);
+ QmlRangeEventData &operator=(const QmlRangeEventData &ref);
};
-struct QMLJSDEBUGCLIENT_EXPORT QmlEventSub {
- QmlEventSub(QmlEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
- QmlEventSub(QmlEventSub *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
- QmlEventData *reference;
+struct QmlRangeEventRelative {
+ QmlRangeEventRelative(QmlRangeEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
+ QmlRangeEventRelative(QmlRangeEventRelative *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
+ QmlRangeEventData *reference;
qint64 duration;
qint64 calls;
bool inLoopPath;
};
-struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
-{
- QV8EventData();
- ~QV8EventData();
-
- QString displayName;
- QString filename;
- QString functionName;
- int line;
- double totalTime; // given in milliseconds
- double totalPercent;
- double selfTime;
- double selfPercent;
- QHash <QString, QV8EventSub *> parentHash;
- QHash <QString, QV8EventSub *> childrenHash;
- int eventId;
-
- QV8EventData &operator=(const QV8EventData &ref);
-};
-
-struct QMLJSDEBUGCLIENT_EXPORT QV8EventSub {
- QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {}
- QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {}
-
- QV8EventData *reference;
- qint64 totalTime;
-};
-
-typedef QHash<QString, QmlEventData *> QmlEventHash;
-typedef QList<QmlEventData *> QmlEventDescriptions;
-typedef QList<QV8EventData *> QV8EventDescriptions;
-
-class QMLJSDEBUGCLIENT_EXPORT QmlProfilerEventList : public QObject
+class QmlProfilerDataModel : public QObject
{
Q_OBJECT
public:
@@ -122,23 +94,30 @@ public:
Done
};
- explicit QmlProfilerEventList(QObject *parent = 0);
- ~QmlProfilerEventList();
+ explicit QmlProfilerDataModel(QObject *parent = 0);
+ ~QmlProfilerDataModel();
- QmlEventDescriptions getEventDescriptions() const;
- QmlEventData *eventDescription(int eventId) const;
- const QV8EventDescriptions& getV8Events() const;
+ QList<QmlRangeEventData *> getEventDescriptions() const;
+ QmlRangeEventData *eventDescription(int eventId) const;
+ QList<QV8EventData *> getV8Events() const;
QV8EventData *v8EventDescription(int eventId) const;
+ static QString getHashStringForQmlEvent(const QmlJsDebugClient::QmlEventLocation &location, int eventType);
+ static QString getHashStringForV8Event(const QString &displayName, const QString &function);
+ static QString rootEventName();
+ static QString rootEventDescription();
+ static QString qmlEventTypeAsString(QmlJsDebugClient::QmlEventType typeEnum);
+ static QmlJsDebugClient::QmlEventType qmlEventTypeAsEnum(const QString &typeString);
+
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
Q_INVOKABLE qint64 firstTimeMark() const;
Q_INVOKABLE qint64 lastTimeMark() const;
- Q_INVOKABLE int count() const;
-
// data access
+ Q_INVOKABLE int count() const;
+ Q_INVOKABLE bool isEmpty() const;
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
@@ -171,7 +150,6 @@ public:
Q_INVOKABLE qint64 qmlMeasuredTime() const;
Q_INVOKABLE qint64 v8MeasuredTime() const;
- void showErrorDialog(const QString &st ) const;
void compileStatistics(qint64 startTime, qint64 endTime);
State currentState() const;
Q_INVOKABLE int getCurrentStateFromQml() const;
@@ -188,44 +166,37 @@ signals:
public slots:
void clear();
+
void addRangedEvent(int type, qint64 startTime, qint64 length,
const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location);
- void complete();
-
void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime);
void addFrameEvent(qint64 time, int framerate, int animationcount);
+ void setTraceStartTime(qint64 time);
+ void setTraceEndTime(qint64 time);
+
+ void complete();
+
bool save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
void load();
- void setTraceEndTime( qint64 time );
- void setTraceStartTime( qint64 time );
-
void rewriteDetailsString(int eventType, const QmlJsDebugClient::QmlEventLocation &location, const QString &newString);
void finishedRewritingDetails();
private:
- void postProcess();
- void sortEndTimes();
- void findAnimationLimits();
- void sortStartTimes();
- void computeLevels();
- void computeNestingLevels();
- void computeNestingDepth();
- void prepareForDisplay();
- void linkEndsToStarts();
- void reloadDetails();
- void findBindingLoops(qint64 startTime, qint64 endTime);
- bool checkBindingLoop(QmlEventData *from, QmlEventData *current, QList<QmlEventData *>visited);
void setState(State state);
+ void reloadDetails();
private:
- class QmlProfilerEventListPrivate;
- QmlProfilerEventListPrivate *d;
+ class QmlProfilerDataModelPrivate;
+ QmlProfilerDataModelPrivate *d;
+
+ friend class QV8ProfilerDataModel;
};
-} // namespace QmlJsDebugClient
+} // namespace Internal
+} // namespace QmlProfiler
-#endif // QMLPROFILEREVENTLIST_H
+#endif // QMLPROFILERDATAMODEL_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
index 561717c890..4f61f8a640 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
@@ -35,7 +35,7 @@
#include <QObject>
-#include "qmljsdebugclient/qmlprofilereventlocation.h"
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
#include <qmljs/qmljsdocument.h>
namespace QmlProfiler {
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.cpp b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
index d16ec2b840..5990df7c4c 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -35,8 +35,6 @@
#include "codaqmlprofilerrunner.h"
#include "localqmlprofilerrunner.h"
#include "remotelinuxqmlprofilerrunner.h"
-#include "qmlprofilerplugin.h"
-#include "qmlprofilertool.h"
#include <analyzerbase/analyzermanager.h>
#include <coreplugin/icore.h>
@@ -76,16 +74,11 @@ public:
QmlProfilerEngine *q;
- //AnalyzerStartParameters m_params;
+ QmlProfilerStateManager *m_profilerState;
+
AbstractQmlProfilerRunner *m_runner;
- bool m_running;
- bool m_fetchingData;
- bool m_hasData;
- bool m_fetchDataFromStart;
- bool m_delayedDelete;
QTimer m_noDebugOutputTimer;
QmlJsDebugClient::QDeclarativeOutputParser m_outputParser;
- QTimer m_runningTimer;
};
AbstractQmlProfilerRunner *
@@ -137,10 +130,7 @@ QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
: IAnalyzerEngine(tool, sp, runConfiguration)
, d(new QmlProfilerEnginePrivate(this))
{
- d->m_running = false;
- d->m_fetchingData = false;
- d->m_fetchDataFromStart = false;
- d->m_delayedDelete = false;
+ d->m_profilerState = 0;
// Only wait 4 seconds for the 'Waiting for connection' on application ouput, then just try to connect
// (application output might be redirected / blocked)
@@ -157,29 +147,31 @@ QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
this, SLOT(processIsRunning()));
connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
this, SLOT(wrongSetupMessageBox(QString)));
-
- d->m_runningTimer.setInterval(100); // ten times per second
- connect(&d->m_runningTimer, SIGNAL(timeout()), this, SIGNAL(timeUpdate()));
}
QmlProfilerEngine::~QmlProfilerEngine()
{
- if (d->m_running)
+ if (d->m_profilerState && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning)
stop();
delete d;
}
bool QmlProfilerEngine::start()
{
+ QTC_ASSERT(d->m_profilerState, return false);
+
if (d->m_runner) {
delete d->m_runner;
d->m_runner = 0;
}
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStarting);
+
if (QmlProjectManager::QmlProjectRunConfiguration *rc =
qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration())) {
if (rc->observerPath().isEmpty()) {
QmlProjectManager::QmlProjectPlugin::showQmlObserverToolWarning();
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
AnalyzerManager::stopTool();
return false;
}
@@ -190,13 +182,14 @@ bool QmlProfilerEngine::start()
if (LocalQmlProfilerRunner *qmlRunner = qobject_cast<LocalQmlProfilerRunner *>(d->m_runner)) {
if (!qmlRunner->hasExecutable()) {
showNonmodalWarning(tr("No executable file to launch."));
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
AnalyzerManager::stopTool();
return false;
}
}
if (d->m_runner) {
- connect(d->m_runner, SIGNAL(stopped()), this, SLOT(stopped()));
+ connect(d->m_runner, SIGNAL(stopped()), this, SLOT(processEnded()));
connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));
d->m_runner->start();
@@ -205,82 +198,80 @@ bool QmlProfilerEngine::start()
emit processRunning(startParameters().connParams.port);
}
-
- d->m_running = true;
- d->m_delayedDelete = false;
- d->m_runningTimer.start();
-
- if (d->m_fetchDataFromStart) {
- d->m_fetchingData = true;
- d->m_hasData = false;
- }
-
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppRunning);
emit starting(this);
return true;
}
void QmlProfilerEngine::stop()
{
- if (d->m_fetchingData) {
- if (d->m_running)
- d->m_delayedDelete = true;
- // will result in dataReceived() call
- emit stopRecording();
- d->m_fetchDataFromStart = true;
- } else {
- finishProcess();
- d->m_fetchDataFromStart = false;
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopRequested);
+ break;
+ }
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::AppKilled : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ }
+ default:
+ qDebug() << tr("Unexpected engine stop from state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ break;
}
}
-void QmlProfilerEngine::stopped()
+void QmlProfilerEngine::processEnded()
{
- // if it was killed, preserve recording flag
- if (d->m_running)
- d->m_fetchDataFromStart = d->m_fetchingData;
-
- // user feedback
- if (d->m_running && d->m_fetchingData && !d->m_hasData) {
- showNonmodalWarning(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
- emit applicationDied();
- }
+ QTC_ASSERT(d->m_profilerState, return);
- d->m_running = false;
- d->m_runningTimer.stop();
- AnalyzerManager::stopTool();
- emit finished();
- emit recordingChanged(d->m_fetchDataFromStart);
-}
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ AnalyzerManager::stopTool();
-void QmlProfilerEngine::setFetchingData(bool b)
-{
- d->m_fetchingData = b;
- if (d->m_running && b)
- d->m_hasData = false;
- if (!d->m_running)
- d->m_fetchDataFromStart = b;
+ emit finished();
+ break;
+ }
+ case QmlProfilerStateManager::AppStopped :
+ // fallthrough
+ case QmlProfilerStateManager::AppKilled : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ }
+ default:
+ qDebug() << tr("Process died unexpectedly from state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ break;
+ }
}
-void QmlProfilerEngine::dataReceived()
+void QmlProfilerEngine::cancelProcess()
{
- if (d->m_delayedDelete)
- finishProcess();
- d->m_delayedDelete = false;
- d->m_hasData = true;
-}
+ QTC_ASSERT(d->m_profilerState, return);
-void QmlProfilerEngine::finishProcess()
-{
- // user stop?
- if (d->m_running) {
- d->m_running = false;
- d->m_runningTimer.stop();
- d->m_noDebugOutputTimer.stop();
- if (d->m_runner)
- d->m_runner->stop();
- emit finished();
- emit recordingChanged(d->m_fetchDataFromStart);
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
+ break;
+ }
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ break;
}
+ default: {
+ qDebug() << tr("Unexpected process termination requested with state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ return;
+ }
+ }
+
+ if (d->m_runner)
+ d->m_runner->stop();
+ emit finished();
}
void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
@@ -306,7 +297,10 @@ void QmlProfilerEngine::wrongSetupMessageBox(const QString &errorMessage)
infoBox->show();
- finishProcess();
+ // KILL
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ AnalyzerManager::stopTool();
+ emit finished();
}
void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
@@ -345,5 +339,44 @@ void QmlProfilerEngine::processIsRunning(quint16 port)
emit processRunning(d->m_runner->debugPort());
}
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerEngine::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ // disconnect old
+ if (d->m_profilerState) {
+ disconnect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ }
+
+ d->m_profilerState = profilerState;
+
+ // connect
+ if (d->m_profilerState) {
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ }
+}
+
+void QmlProfilerEngine::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::Idle : {
+ // for some reason the engine is not deleted when it goes to idle
+ // a new one will be created on the next run, and this one will
+ // be only deleted if the new one is running the same app
+
+ // we need to explictly disconnect it here without expecting a deletion
+ // as it will not be run any more, otherwise we will get funny side effects
+ registerProfilerStateManager(0);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
} // namespace Internal
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.h b/src/plugins/qmlprofiler/qmlprofilerengine.h
index b0199c3131..c438e69329 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.h
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.h
@@ -34,6 +34,7 @@
#define QMLPROFILERENGINE_H
#include <analyzerbase/ianalyzerengine.h>
+#include "qmlprofilerstatemanager.h"
#include <utils/outputformat.h>
namespace QmlProfiler {
@@ -49,29 +50,29 @@ public:
ProjectExplorer::RunConfiguration *runConfiguration);
~QmlProfilerEngine();
+ void registerProfilerStateManager( QmlProfilerStateManager *profilerState );
+
static void showNonmodalWarning(const QString &warningMsg);
signals:
- void processRunning(int port);
- void stopRecording();
+ void processRunning(quint16 port);
void timeUpdate();
- void recordingChanged(bool recording);
- void applicationDied();
public slots:
bool start();
void stop();
private slots:
- void stopped();
+ void processEnded();
- void setFetchingData(bool);
- void dataReceived();
- void finishProcess();
+ void cancelProcess();
void logApplicationMessage(const QString &msg, Utils::OutputFormat format);
void wrongSetupMessageBox(const QString &errorMessage);
void wrongSetupMessageBoxFinished(int);
void processIsRunning(quint16 port = 0);
+private slots:
+ void profilerStateChanged();
+
private:
class QmlProfilerEnginePrivate;
QmlProfilerEnginePrivate *d;
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index 294c4ed555..169635fec9 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -48,6 +48,11 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
using namespace QmlJsDebugClient;
@@ -90,31 +95,63 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsWidget::QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent) : QWidget(parent)
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
{
- setObjectName("QmlProfilerEventsView");
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
+
+ QmlProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
+ QmlProfilerEventsParentsAndChildrenView *m_eventParents;
+ QmlProfilerDataModel *m_profilerDataModel;
- m_eventTree = new QmlProfilerEventsMainView(model, this);
- m_eventTree->setViewType(QmlProfilerEventsMainView::EventsView);
- connect(m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
- connect(m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+ bool m_globalStatsEnabled;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerDataModel *profilerDataModel )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName("QmlProfilerEventsView");
- m_eventChildren = new QmlProfilerEventsParentsAndChildrenView(model, QmlProfilerEventsParentsAndChildrenView::ChildrenView, this);
- m_eventParents = new QmlProfilerEventsParentsAndChildrenView(model, QmlProfilerEventsParentsAndChildrenView::ParentsView, this);
- connect(m_eventTree, SIGNAL(eventSelected(int)), m_eventChildren, SLOT(displayEvent(int)));
- connect(m_eventTree, SIGNAL(eventSelected(int)), m_eventParents, SLOT(displayEvent(int)));
- connect(m_eventChildren, SIGNAL(eventClicked(int)), m_eventTree, SLOT(selectEvent(int)));
- connect(m_eventParents, SIGNAL(eventClicked(int)), m_eventTree, SLOT(selectEvent(int)));
+ d->m_profilerDataModel = profilerDataModel;
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(QmlProfilerEventsMainView::EventsView, this, d->m_profilerDataModel);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventsParentsAndChildrenView(
+ QmlProfilerEventsParentsAndChildrenView::ChildrenView,
+ this,
+ d->m_profilerDataModel);
+ d->m_eventParents = new QmlProfilerEventsParentsAndChildrenView(
+ QmlProfilerEventsParentsAndChildrenView::ParentsView,
+ this,
+ d->m_profilerDataModel);
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
// widget arrangement
QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->setContentsMargins(0,0,0,0);
groupLayout->setSpacing(0);
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
- splitterVertical->addWidget(m_eventTree);
+ splitterVertical->addWidget(d->m_eventTree);
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
- splitterHorizontal->addWidget(m_eventParents);
- splitterHorizontal->addWidget(m_eventChildren);
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
splitterHorizontal->setOrientation(Qt::Horizontal);
splitterVertical->addWidget(splitterHorizontal);
splitterVertical->setOrientation(Qt::Vertical);
@@ -123,23 +160,21 @@ QmlProfilerEventsWidget::QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEv
groupLayout->addWidget(splitterVertical);
setLayout(groupLayout);
- m_eventStatistics = model;
- if (model) {
- connect(model, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- }
-
- m_globalStatsEnabled = true;
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_globalStatsEnabled = true;
}
QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
{
+ delete d;
}
-void QmlProfilerEventsWidget::eventListStateChanged()
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
{
- if (m_eventStatistics) {
- QmlProfilerEventList::State newState = m_eventStatistics->currentState();
- if (newState == QmlProfilerEventList::Empty) {
+ if (d->m_profilerDataModel) {
+ QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
+ if (newState == QmlProfilerDataModel::Empty) {
clear();
}
}
@@ -148,57 +183,119 @@ void QmlProfilerEventsWidget::eventListStateChanged()
void QmlProfilerEventsWidget::switchToV8View()
{
setObjectName("QmlProfilerV8ProfileView");
- m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
- m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
- m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
+ d->m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
+ d->m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
+ d->m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
setToolTip(tr("Trace information from the v8 JavaScript engine. Available only in Qt5 based applications"));
}
void QmlProfilerEventsWidget::clear()
{
- m_eventTree->clear();
- m_eventChildren->clear();
- m_eventParents->clear();
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
}
void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
clear();
- m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
- m_globalStatsEnabled = m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
+ d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
+ d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
}
QModelIndex QmlProfilerEventsWidget::selectedItem() const
{
- return m_eventTree->selectedItem();
+ return d->m_eventTree->selectedItem();
}
void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
{
- emit contextMenuRequested(ev->globalPos());
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ if (isQml()) {
+ // only for qml events view, not for v8
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+ }
+
+ if (isQml()) {
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+ }
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction) {
+ if (d->m_profilerDataModel) {
+ getStatisticsInRange(d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ }
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
}
bool QmlProfilerEventsWidget::mouseOnTable(const QPoint &position) const
{
- QPoint tableTopLeft = m_eventTree->mapToGlobal(QPoint(0,0));
- QPoint tableBottomRight = m_eventTree->mapToGlobal(QPoint(m_eventTree->width(), m_eventTree->height()));
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
}
void QmlProfilerEventsWidget::copyTableToClipboard() const
{
- m_eventTree->copyTableToClipboard();
+ d->m_eventTree->copyTableToClipboard();
}
void QmlProfilerEventsWidget::copyRowToClipboard() const
{
- m_eventTree->copyRowToClipboard();
+ d->m_eventTree->copyRowToClipboard();
}
void QmlProfilerEventsWidget::updateSelectedEvent(int eventId) const
{
- if (m_eventTree->selectedEventId() != eventId)
- m_eventTree->selectEvent(eventId);
+ if (d->m_eventTree->selectedEventId() != eventId)
+ d->m_eventTree->selectEvent(eventId);
}
void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
@@ -207,22 +304,31 @@ void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, in
// Our javascript trace data does not store column information
// thus we ignore it here
Q_UNUSED(column);
- m_eventTree->selectEventByLocation(filename, line);
+ d->m_eventTree->selectEventByLocation(filename, line);
}
bool QmlProfilerEventsWidget::hasGlobalStats() const
{
- return m_globalStatsEnabled;
+ return d->m_globalStatsEnabled;
}
void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
{
- m_eventTree->setShowExtendedStatistics(show);
+ d->m_eventTree->setShowExtendedStatistics(show);
}
bool QmlProfilerEventsWidget::showExtendedStatistics() const
{
- return m_eventTree->showExtendedStatistics();
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+bool QmlProfilerEventsWidget::isQml() const
+{
+ return d->m_eventTree->viewType() == QmlProfilerEventsMainView::EventsView;
+}
+bool QmlProfilerEventsWidget::isV8() const
+{
+ return d->m_eventTree->viewType() == QmlProfilerEventsMainView::V8ProfileView;
}
////////////////////////////////////////////////////////////////////////////////////
@@ -232,8 +338,8 @@ class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
public:
QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
- void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() );
- void buildV8ModelFromList( const QV8EventDescriptions &list );
+ void buildModelFromList(const QList<QmlRangeEventData *> &list, QStandardItem *parentItem );
+ void buildV8ModelFromList( const QList<QV8EventData *> &list );
int getFieldCount();
QString textForItem(QStandardItem *item, bool recursive) const;
@@ -242,7 +348,7 @@ public:
QmlProfilerEventsMainView *q;
QmlProfilerEventsMainView::ViewTypes m_viewType;
- QmlProfilerEventList *m_eventStatistics;
+ QmlProfilerDataModel *m_profilerDataModel;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
QHash<int, int> m_columnIndex; // maps field enum to column index
@@ -254,8 +360,10 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsMainView::QmlProfilerEventsMainView(QmlProfilerEventList *model, QWidget *parent) :
- QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(ViewTypes viewType,
+ QWidget *parent,
+ QmlProfilerDataModel *dataModel)
+ : QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
{
setObjectName("QmlProfilerEventsTable");
header()->setResizeMode(QHeaderView::Interactive);
@@ -268,15 +376,17 @@ QmlProfilerEventsMainView::QmlProfilerEventsMainView(QmlProfilerEventList *model
setModel(d->m_model);
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
- d->m_eventStatistics = 0;
- setEventStatisticsModel(model);
+ d->m_profilerDataModel = dataModel;
+ connect(d->m_profilerDataModel,SIGNAL(stateChanged()),
+ this,SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerDataModel,SIGNAL(detailsChanged(int,QString)),
+ this,SLOT(changeDetailsForEvent(int,QString)));
d->m_firstNumericColumn = 0;
d->m_preventSelectBounce = false;
d->m_showExtendedStatistics = false;
- // default view
- setViewType(EventsView);
+ setViewType(viewType);
}
QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
@@ -286,24 +396,11 @@ QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
delete d;
}
-void QmlProfilerEventsMainView::setEventStatisticsModel( QmlProfilerEventList *model )
-{
- if (d->m_eventStatistics) {
- disconnect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
- disconnect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
- }
- d->m_eventStatistics = model;
- if (model) {
- connect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
- connect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
- }
-}
-
-void QmlProfilerEventsMainView::eventListStateChanged()
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
{
- if (d->m_eventStatistics) {
- QmlProfilerEventList::State newState = d->m_eventStatistics->currentState();
- if (newState == QmlProfilerEventList::Done)
+ if (d->m_profilerDataModel) {
+ QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
+ if (newState == QmlProfilerDataModel::Done)
buildModel();
}
}
@@ -320,6 +417,11 @@ void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
}
}
+QmlProfilerEventsMainView::ViewTypes QmlProfilerEventsMainView::viewType() const
+{
+ return d->m_viewType;
+}
+
void QmlProfilerEventsMainView::setViewType(ViewTypes type)
{
d->m_viewType = type;
@@ -466,12 +568,12 @@ int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
void QmlProfilerEventsMainView::buildModel()
{
- if (d->m_eventStatistics) {
+ if (d->m_profilerDataModel) {
clear();
if (d->m_viewType == V8ProfileView)
- d->buildV8ModelFromList( d->m_eventStatistics->getV8Events() );
+ d->buildV8ModelFromList( d->m_profilerDataModel->getV8Events() );
else
- d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
+ d->buildModelFromList( d->m_profilerDataModel->getEventDescriptions(), d->m_model->invisibleRootItem() );
setShowExtendedStatistics(d->m_showExtendedStatistics);
@@ -489,18 +591,15 @@ void QmlProfilerEventsMainView::buildModel()
}
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList )
+void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QList<QmlRangeEventData *> &list, QStandardItem *parentItem)
{
- foreach (QmlEventData *binding, list) {
- if (visitedFunctionsList.contains(binding))
- continue;
-
+ foreach (QmlRangeEventData *binding, list) {
if (binding->calls == 0)
continue;
QList<QStandardItem *> newRow;
if (m_fieldShown[Name]) {
- newRow << new EventsViewItem(binding->displayname);
+ newRow << new EventsViewItem(binding->displayName);
}
if (m_fieldShown[Type]) {
@@ -573,7 +672,7 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
}
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QV8EventDescriptions &list)
+void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
{
for (int index = 0; index < list.count(); index++) {
QV8EventData *v8event = list.at(index);
@@ -650,13 +749,17 @@ QString QmlProfilerEventsMainView::nameForType(int typeNumber)
void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
- d->m_eventStatistics->compileStatistics(rangeStart, rangeEnd);
+ if (d->m_profilerDataModel)
+ d->m_profilerDataModel->compileStatistics(rangeStart, rangeEnd);
buildModel();
}
bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const
{
- return d->m_eventStatistics->traceStartTime() == rangeStart && d->m_eventStatistics->traceEndTime() == rangeEnd;
+ if (d->m_profilerDataModel)
+ return d->m_profilerDataModel->traceStartTime() == rangeStart && d->m_profilerDataModel->traceEndTime() == rangeEnd;
+ else
+ return true;
}
int QmlProfilerEventsMainView::selectedEventId() const
@@ -692,7 +795,7 @@ void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
// show in callers/callees subwindow
emit eventSelected(infoItem->data(EventIdRole).toInt());
- // show in timelineview
+ // show in timelinerenderer
if (d->m_viewType == EventsView) {
emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
}
@@ -820,9 +923,11 @@ void QmlProfilerEventsMainView::copyRowToClipboard() const
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView(QmlJsDebugClient::QmlProfilerEventList *model, SubViewType subtableType, QWidget *parent):QTreeView(parent)
+QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView(
+ SubViewType subtableType, QWidget *parent, QmlProfilerDataModel *model)
+ : QTreeView(parent)
{
- m_eventList = model;
+ m_profilerDataModel = model;
setModel(new QStandardItemModel(this));
setRootIsDecorated(false);
setFrameStyle(QFrame::NoFrame);
@@ -844,30 +949,33 @@ void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type)
void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
{
+ if (!m_profilerDataModel)
+ return;
+
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
if (isV8) {
- QmlJsDebugClient::QV8EventData *v8event = m_eventList->v8EventDescription(eventId);
+ QV8EventData *v8event = m_profilerDataModel->v8EventDescription(eventId);
if (v8event) {
if (isChildren) {
- QList <QmlJsDebugClient::QV8EventSub *> childrenList = v8event->childrenHash.values();
+ QList <QV8EventSub *> childrenList = v8event->childrenHash.values();
rebuildTree((QObject *)&childrenList);
}
else {
- QList <QmlJsDebugClient::QV8EventSub *> parentList = v8event->parentHash.values();
+ QList <QV8EventSub *> parentList = v8event->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
} else {
- QmlJsDebugClient::QmlEventData *qmlEvent = m_eventList->eventDescription(eventId);
+ QmlRangeEventData *qmlEvent = m_profilerDataModel->eventDescription(eventId);
if (qmlEvent) {
if (isChildren) {
- QList <QmlJsDebugClient::QmlEventSub *> childrenList = qmlEvent->childrenHash.values();
+ QList <QmlRangeEventRelative *> childrenList = qmlEvent->childrenHash.values();
rebuildTree((QObject *)&childrenList);
}
else {
- QList <QmlJsDebugClient::QmlEventSub *> parentList = qmlEvent->parentHash.values();
+ QList <QmlRangeEventRelative *> parentList = qmlEvent->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
@@ -882,7 +990,7 @@ void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
sortByColumn(2);
}
-void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
+void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel)
{
Q_ASSERT(treeModel());
treeModel()->clear();
@@ -890,8 +998,8 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
- QList <QmlEventSub *> *qmlList = static_cast< QList <QmlEventSub *> *>(eventList);
- QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(eventList);
+ QList <QmlRangeEventRelative *> *qmlList = static_cast< QList <QmlRangeEventRelative *> *>(profilerDataModel);
+ QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(profilerDataModel);
int listLength;
if (!isV8)
@@ -902,9 +1010,9 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
for (int index=0; index < listLength; index++) {
QList<QStandardItem *> newRow;
if (!isV8) {
- QmlEventSub *event = qmlList->at(index);
+ QmlRangeEventRelative *event = qmlList->at(index);
- newRow << new EventsViewItem(event->reference->displayname);
+ newRow << new EventsViewItem(event->reference->displayName);
newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
newRow << new EventsViewItem(QString::number(event->calls));
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index 1c7c87d7f2..409ea4dca0 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -34,9 +34,13 @@
#define QMLPROFILEREVENTVIEW_H
#include <QTreeView>
-#include <qmljsdebugclient/qmlprofilereventtypes.h>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
#include <QStandardItemModel>
+#include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include "qmlprofilerdatamodel.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
namespace QmlProfiler {
namespace Internal {
@@ -44,9 +48,6 @@ namespace Internal {
class QmlProfilerEventsMainView;
class QmlProfilerEventsParentsAndChildrenView;
-typedef QHash<QString, QmlJsDebugClient::QmlEventData *> QmlEventHash;
-typedef QList<QmlJsDebugClient::QmlEventData *> QmlEventList;
-
enum ItemRole {
EventHashStrRole = Qt::UserRole+1,
FilenameRole = Qt::UserRole+2,
@@ -59,7 +60,10 @@ class QmlProfilerEventsWidget : public QWidget
{
Q_OBJECT
public:
- explicit QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent);
+ explicit QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerDataModel *profilerDataModel );
~QmlProfilerEventsWidget();
void switchToV8View();
@@ -75,9 +79,11 @@ public:
void setShowExtendedStatistics(bool show);
bool showExtendedStatistics() const;
+ bool isQml() const;
+ bool isV8() const;
+
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
- void contextMenuRequested(const QPoint &position);
void showEventInTimeline(int eventId);
public slots:
@@ -85,18 +91,14 @@ public slots:
void selectBySourceLocation(const QString &filename, int line, int column);
private slots:
- void eventListStateChanged();
+ void profilerDataModelStateChanged();
protected:
void contextMenuEvent(QContextMenuEvent *ev);
private:
- QmlProfilerEventsMainView *m_eventTree;
- QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
- QmlProfilerEventsParentsAndChildrenView *m_eventParents;
- QmlJsDebugClient::QmlProfilerEventList *m_eventStatistics;
-
- bool m_globalStatsEnabled;
+ class QmlProfilerEventsWidgetPrivate;
+ QmlProfilerEventsWidgetPrivate *d;
};
class QmlProfilerEventsMainView : public QTreeView
@@ -129,12 +131,14 @@ public:
MaxViewTypes
};
- explicit QmlProfilerEventsMainView(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent);
+ explicit QmlProfilerEventsMainView(ViewTypes viewType,
+ QWidget *parent,
+ QmlProfilerDataModel *dataModel);
~QmlProfilerEventsMainView();
- void setEventStatisticsModel(QmlJsDebugClient::QmlProfilerEventList *model);
void setFieldViewable(Fields field, bool show);
void setViewType(ViewTypes type);
+ ViewTypes viewType() const;
void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const;
@@ -157,7 +161,6 @@ signals:
void showEventInTimeline(int eventId);
public slots:
- void eventListStateChanged();
void clear();
void jumpToItem(const QModelIndex &index);
void selectEvent(int eventId);
@@ -165,6 +168,9 @@ public slots:
void buildModel();
void changeDetailsForEvent(int eventId, const QString &newString);
+private slots:
+ void profilerDataModelStateChanged();
+
private:
void setHeaderLabels();
@@ -186,7 +192,9 @@ public:
MaxSubtableTypes
};
- explicit QmlProfilerEventsParentsAndChildrenView(QmlJsDebugClient::QmlProfilerEventList *model, SubViewType subtableType, QWidget *parent);
+ explicit QmlProfilerEventsParentsAndChildrenView(SubViewType subtableType,
+ QWidget *parent,
+ QmlProfilerDataModel *model);
~QmlProfilerEventsParentsAndChildrenView();
void setViewType(SubViewType type);
@@ -200,10 +208,10 @@ public slots:
void clear();
private:
- void rebuildTree(void *eventList);
+ void rebuildTree(void *profilerDataModel);
void updateHeader();
QStandardItemModel *treeModel();
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
+ QmlProfilerDataModel *m_profilerDataModel;
SubViewType m_subtableType;
};
diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.h b/src/plugins/qmlprofiler/qmlprofilerplugin.h
index 624d2a6988..6e0512e2e0 100644
--- a/src/plugins/qmlprofiler/qmlprofilerplugin.h
+++ b/src/plugins/qmlprofiler/qmlprofilerplugin.h
@@ -43,6 +43,7 @@ namespace Internal {
class QmlProfilerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlProfiler.json")
public:
QmlProfilerPlugin() {}
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
new file mode 100644
index 0000000000..194e2cdf1c
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
@@ -0,0 +1,163 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerstatemanager.h"
+
+#include <QDebug>
+#include <utils/qtcassert.h>
+
+// uncomment for printing the state changes to debug output
+//#define _DEBUG_PROFILERSTATE_
+
+namespace QmlProfiler {
+namespace Internal {
+
+inline QString stringForState(int state) {
+ switch (state) {
+ case QmlProfilerStateManager::Idle: return QString("Idle");
+ case QmlProfilerStateManager::AppStarting: return QString("AppStarting");
+ case QmlProfilerStateManager::AppRunning: return QString("AppRunning");
+ case QmlProfilerStateManager::AppStopRequested: return QString("AppStopRequested");
+ case QmlProfilerStateManager::AppReadyToStop: return QString("AppReadyToStop");
+ case QmlProfilerStateManager::AppStopped: return QString("AppStopped");
+ case QmlProfilerStateManager::AppKilled: return QString("AppKilled");
+ default: break;
+ }
+ return QString();
+}
+
+class QmlProfilerStateManager::QmlProfilerStateManagerPrivate
+{
+public:
+ QmlProfilerStateManagerPrivate(QmlProfilerStateManager *qq) : q(qq) {}
+ ~QmlProfilerStateManagerPrivate() {}
+
+ QmlProfilerStateManager *q;
+
+ QmlProfilerStateManager::QmlProfilerState m_currentState;
+ bool m_clientRecording;
+ bool m_serverRecording;
+};
+QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) :
+ QObject(parent),d(new QmlProfilerStateManagerPrivate(this))
+{
+ d->m_currentState = Idle;
+ d->m_clientRecording = true;
+ d->m_serverRecording = false;
+}
+
+QmlProfilerStateManager::~QmlProfilerStateManager()
+{
+ delete d;
+}
+
+QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState()
+{
+ return d->m_currentState;
+}
+
+bool QmlProfilerStateManager::clientRecording()
+{
+ return d->m_clientRecording;
+}
+
+bool QmlProfilerStateManager::serverRecording()
+{
+ return d->m_serverRecording;
+}
+
+QString QmlProfilerStateManager::currentStateAsString()
+{
+ return stringForState(d->m_currentState);
+}
+
+void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Profiler state change request from" << stringForState(d->m_currentState) << "to" << stringForState(newState);
+#endif
+ QTC_ASSERT(d->m_currentState != newState, /**/);
+ switch (newState) {
+ case Idle:
+ QTC_ASSERT(d->m_currentState == AppStarting || d->m_currentState == AppStopped || d->m_currentState == AppKilled, /**/);
+ break;
+ case AppStarting:
+ QTC_ASSERT(d->m_currentState == Idle, /**/);
+ break;
+ case AppRunning:
+ QTC_ASSERT(d->m_currentState == AppStarting, /**/);
+ break;
+ case AppStopRequested:
+ QTC_ASSERT(d->m_currentState == AppRunning, /**/);
+ break;
+ case AppReadyToStop:
+ QTC_ASSERT(d->m_currentState == AppStopRequested, /**/);
+ break;
+ case AppStopped:
+ QTC_ASSERT(d->m_currentState == AppReadyToStop, /**/);
+ break;
+ case AppKilled:
+ QTC_ASSERT(d->m_currentState == AppRunning, /**/);
+ break;
+ default:
+ qDebug() << tr("Switching to unknown state in %1:%2").arg(QString(__FILE__), QString::number(__LINE__));
+ break;
+ }
+
+ d->m_currentState = newState;
+ emit stateChanged();
+}
+
+void QmlProfilerStateManager::setClientRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting client recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_clientRecording != recording) {
+ d->m_clientRecording = recording;
+ emit clientRecordingChanged();
+ }
+}
+
+void QmlProfilerStateManager::setServerRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting server recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_serverRecording != recording) {
+ d->m_serverRecording = recording;
+ emit serverRecordingChanged();
+ }
+}
+
+}
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatemanager.h b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
new file mode 100644
index 0000000000..188b0b5be3
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
@@ -0,0 +1,82 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILERSTATEMANAGER_H
+#define QMLPROFILERSTATEMANAGER_H
+
+#include <QObject>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateManager : public QObject
+{
+ Q_OBJECT
+public:
+ enum QmlProfilerState {
+ Idle,
+ AppStarting,
+ AppRunning,
+ AppStopRequested,
+ AppReadyToStop,
+ AppStopped,
+ AppKilled
+ };
+
+ explicit QmlProfilerStateManager(QObject *parent = 0);
+ ~QmlProfilerStateManager();
+
+ QmlProfilerState currentState();
+ bool clientRecording();
+ bool serverRecording();
+
+ QString currentStateAsString();
+
+signals:
+ void stateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+public slots:
+ void setCurrentState(QmlProfilerState newState);
+ void setClientRecording(bool recording);
+ void setServerRecording(bool recording);
+
+private:
+ class QmlProfilerStateManagerPrivate;
+ QmlProfilerStateManagerPrivate *d;
+};
+
+}
+}
+
+#endif // QMLPROFILERSTATEMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index fa01ea592d..6a16546e1a 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -31,24 +31,20 @@
**************************************************************************/
#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
#include "qmlprofilerengine.h"
-#include "qmlprofilerplugin.h"
#include "qmlprofilerconstants.h"
#include "qmlprofilerattachdialog.h"
-#include "qmlprofilereventview.h"
-
-#include "tracewindow.h"
-#include "timelineview.h"
-
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilerdatamodel.h"
+#include "qmlprofilerdetailsrewriter.h"
+#include "timelinerenderer.h"
#include <analyzerbase/analyzermanager.h>
-#include <analyzerbase/analyzerconstants.h>
#include <analyzerbase/analyzerruncontrol.h>
-#include "canvas/qdeclarativecanvas_p.h"
-#include "canvas/qdeclarativecanvastimer_p.h"
+#include "canvas/qdeclarativecontext2d_p.h"
#include "canvas/qmlprofilercanvas.h"
#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
@@ -56,7 +52,6 @@
#include <utils/fileinprojectfinder.h>
#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
@@ -65,7 +60,6 @@
#include <remotelinux/remotelinuxrunconfiguration.h>
#include <remotelinux/linuxdeviceconfiguration.h>
-#include <texteditor/itexteditor.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
@@ -82,23 +76,22 @@
#include <qt4projectmanager/qt-s60/s60devicerunconfiguration.h>
#include <qt4projectmanager/qt-s60/s60deployconfiguration.h>
-#include <QFile>
-
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
-#include <QTabWidget>
#include <QToolButton>
#include <QMessageBox>
-#include <QDockWidget>
#include <QFileDialog>
#include <QMenu>
+#include <QTimer>
+#include <QTime>
using namespace Core;
using namespace Core::Constants;
using namespace Analyzer;
using namespace Analyzer::Constants;
using namespace QmlProfiler::Internal;
+using namespace QmlProfiler::Constants;
using namespace QmlJsDebugClient;
using namespace ProjectExplorer;
using namespace QmlProjectManager;
@@ -112,59 +105,67 @@ public:
QmlProfilerTool *q;
- QDeclarativeDebugConnection *m_client;
- QTimer m_connectionTimer;
- int m_connectionAttempts;
- TraceWindow *m_traceWindow;
- QmlProfilerEventsWidget *m_eventsView;
- QmlProfilerEventsWidget *m_v8profilerView;
+ QmlProfilerStateManager *m_profilerState;
+ QmlProfilerClientManager *m_profilerConnections;
+ QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerDetailsRewriter *m_detailsRewriter;
+
+ QmlProfilerViewManager *m_viewContainer;
Utils::FileInProjectFinder m_projectFinder;
RunConfiguration *m_runConfiguration;
- bool m_isAttached;
QToolButton *m_recordButton;
QToolButton *m_clearButton;
- bool m_recordingEnabled;
- bool m_appIsRunning;
- bool m_qmlActive;
- bool m_v8Active;
- QTime m_appTimer;
- qint64 m_appRunningTime;
-
- enum ConnectMode {
- TcpConnection, OstConnection
- };
-
- ConnectMode m_connectMode;
- QString m_tcpHost;
- quint64 m_tcpPort;
- QString m_ostDevice;
- QString m_sysroot;
+
+ // elapsed time display
+ QTimer m_recordingTimer;
+ QTime m_recordingElapsedTime;
+ QLabel *m_timeLabel;
+
+ // save and load actions
QAction *m_saveQmlTrace;
+ QAction *m_loadQmlTrace;
};
QmlProfilerTool::QmlProfilerTool(QObject *parent)
: IAnalyzerTool(parent), d(new QmlProfilerToolPrivate(this))
{
setObjectName("QmlProfilerTool");
- d->m_client = 0;
- d->m_connectionAttempts = 0;
- d->m_traceWindow = 0;
- d->m_runConfiguration = 0;
- d->m_isAttached = false;
- d->m_recordingEnabled = true;
- d->m_appIsRunning = false;
- d->m_appTimer.start();
- d->m_appRunningTime = 0;
- d->m_connectionTimer.setInterval(200);
- connect(&d->m_connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
+ d->m_profilerState = 0;
+ d->m_viewContainer = 0;
+ d->m_runConfiguration = 0;
- qmlRegisterType<Canvas>("Monitor", 1, 0, "Canvas");
qmlRegisterType<QmlProfilerCanvas>("Monitor", 1, 0, "Canvas2D");
qmlRegisterType<Context2D>();
- qmlRegisterType<CanvasImage>();
qmlRegisterType<CanvasGradient>();
- qmlRegisterType<TimelineView>("Monitor", 1, 0,"TimelineView");
+ qmlRegisterType<TimelineRenderer>("Monitor", 1, 0,"TimelineRenderer");
+
+ d->m_profilerState = new QmlProfilerStateManager(this);
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()), this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()), this, SLOT(serverRecordingChanged()));
+
+ d->m_profilerConnections = new QmlProfilerClientManager(this);
+ d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
+
+ d->m_profilerDataModel = new QmlProfilerDataModel(this);
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerDataModel, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
+ connect(d->m_profilerConnections, SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)), d->m_profilerDataModel, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->m_profilerConnections, SIGNAL(addV8Event(int,QString,QString,int,double,double)), d->m_profilerDataModel, SLOT(addV8Event(int,QString,QString,int,double,double)));
+ connect(d->m_profilerConnections, SIGNAL(addFrameEvent(qint64,int,int)), d->m_profilerDataModel, SLOT(addFrameEvent(qint64,int,int)));
+ connect(d->m_profilerConnections, SIGNAL(traceStarted(qint64)), d->m_profilerDataModel, SLOT(setTraceStartTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(traceFinished(qint64)), d->m_profilerDataModel, SLOT(setTraceEndTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(dataReadyForProcessing()), d->m_profilerDataModel, SLOT(complete()));
+
+
+ d->m_detailsRewriter = new QmlProfilerDetailsRewriter(this);
+ connect(d->m_profilerDataModel, SIGNAL(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)),
+ d->m_detailsRewriter, SLOT(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)),
+ d->m_profilerDataModel, SLOT(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)));
+ connect(d->m_detailsRewriter, SIGNAL(eventDetailsChanged()), d->m_profilerDataModel, SLOT(finishedRewritingDetails()));
+ connect(d->m_profilerDataModel, SIGNAL(reloadDocumentsForDetails()), d->m_detailsRewriter, SLOT(reloadDocuments()));
Command *command = 0;
const Context globalContext(C_GLOBAL);
@@ -176,7 +177,7 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
menu->addMenu(options, G_ANALYZER_OPTIONS);
options->menu()->setEnabled(true);
- QAction *act = new QAction(tr("Load QML Trace"), options);
+ QAction *act = d->m_loadQmlTrace = new QAction(tr("Load QML Trace"), options);
command = am->registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.LoadQMLTrace", globalContext);
connect(act, SIGNAL(triggered()), this, SLOT(showLoadDialog()));
options->addAction(command);
@@ -186,11 +187,13 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
command = am->registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.SaveQMLTrace", globalContext);
connect(act, SIGNAL(triggered()), this, SLOT(showSaveDialog()));
options->addAction(command);
+
+ d->m_recordingTimer.setInterval(100);
+ connect(&d->m_recordingTimer, SIGNAL(timeout()), this, SLOT(updateTimeDisplay()));
}
QmlProfilerTool::~QmlProfilerTool()
{
- delete d->m_client;
delete d;
}
@@ -220,86 +223,14 @@ IAnalyzerTool::ToolMode QmlProfilerTool::toolMode() const
return AnyMode;
}
-void QmlProfilerTool::showContextMenu(const QPoint &position)
-{
- QmlProfilerEventsWidget *eventView = qobject_cast<QmlProfilerEventsWidget *>(sender());
- TraceWindow *traceView = qobject_cast<TraceWindow *>(sender());
-
- QMenu menu;
- QAction *loadAction = menu.addAction(tr("Load QML Trace"));
- QAction *saveAction = menu.addAction(tr("Save QML Trace"));
- QAction *copyRowAction = 0;
- QAction *copyTableAction = 0;
- QAction *showExtendedStatsAction = 0;
- QAction *viewAllAction = 0;
- QAction *getLocalStatsAction = 0;
- QAction *getGlobalStatsAction = 0;
-
- if (eventView && eventView->mouseOnTable(position)) {
- menu.addSeparator();
- if (eventView->selectedItem().isValid())
- copyRowAction = menu.addAction(tr("Copy Row"));
- copyTableAction = menu.addAction(tr("Copy Table"));
-
- if (eventView == d->m_eventsView) {
- // only for qml events view, not for v8
- showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
- showExtendedStatsAction->setCheckable(true);
- showExtendedStatsAction->setChecked(eventView->showExtendedStatistics());
- }
- }
-
- if (sender() == d->m_traceWindow || sender() == d->m_eventsView) {
- menu.addSeparator();
- getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
- if (!d->m_traceWindow->hasValidSelection())
- getLocalStatsAction->setEnabled(false);
- getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
- if (d->m_eventsView->hasGlobalStats())
- getGlobalStatsAction->setEnabled(false);
- }
-
- if (traceView) {
- if (traceView->getEventList()->count() > 0) {
- menu.addSeparator();
- viewAllAction = menu.addAction(tr("Reset Zoom"));
- }
- }
-
- QAction *selectedAction = menu.exec(position);
-
- if (selectedAction) {
- if (selectedAction == loadAction)
- showLoadDialog();
- if (selectedAction == saveAction)
- showSaveDialog();
- if (selectedAction == copyRowAction)
- eventView->copyRowToClipboard();
- if (selectedAction == copyTableAction)
- eventView->copyTableToClipboard();
- if (selectedAction == viewAllAction)
- traceView->viewAll();
- if (selectedAction == getLocalStatsAction) {
- d->m_eventsView->getStatisticsInRange(
- d->m_traceWindow->selectionStart(),
- d->m_traceWindow->selectionEnd());
- }
- if (selectedAction == getGlobalStatsAction) {
- d->m_eventsView->getStatisticsInRange(
- d->m_traceWindow->getEventList()->traceStartTime(),
- d->m_traceWindow->getEventList()->traceEndTime());
- }
- if (selectedAction == showExtendedStatsAction)
- eventView->setShowExtendedStatistics(!eventView->showExtendedStatistics());
- }
-}
-
IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp,
RunConfiguration *runConfiguration)
{
QmlProfilerEngine *engine = new QmlProfilerEngine(this, sp, runConfiguration);
- d->m_connectMode = QmlProfilerToolPrivate::TcpConnection;
+ engine->registerProfilerStateManager(d->m_profilerState);
+
+ bool isTcpConnection = true;
if (runConfiguration) {
// Check minimum Qt Version. We cannot really be sure what the Qt version
@@ -324,16 +255,15 @@ IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp
runConfiguration->target()->activeDeployConfiguration())) {
if (deployConfig->communicationChannel()
== Qt4ProjectManager::S60DeployConfiguration::CommunicationCodaSerialConnection) {
- d->m_connectMode = QmlProfilerToolPrivate::OstConnection;
- d->m_ostDevice = deployConfig->serialPortName();
+ d->m_profilerConnections->setOstConnection(deployConfig->serialPortName());
+ isTcpConnection = false;
}
}
}
// FIXME: Check that there's something sensible in sp.connParams
- if (d->m_connectMode == QmlProfilerToolPrivate::TcpConnection) {
- d->m_tcpHost = sp.connParams.host;
- d->m_tcpPort = sp.connParams.port;
+ if (isTcpConnection) {
+ d->m_profilerConnections->setTcpConnection(sp.connParams.host, sp.connParams.port);
}
d->m_runConfiguration = runConfiguration;
@@ -364,20 +294,9 @@ IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp
d->m_projectFinder.setProjectFiles(sourceFiles);
d->m_projectFinder.setSysroot(sp.sysroot);
- connect(engine, SIGNAL(processRunning(int)), this, SLOT(connectClient(int)));
- connect(engine, SIGNAL(finished()), this, SLOT(disconnectClient()));
- connect(engine, SIGNAL(finished()), this, SLOT(updateTimers()));
- connect(engine, SIGNAL(stopRecording()), this, SLOT(stopRecording()));
- connect(engine, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
- connect(engine, SIGNAL(timeUpdate()), this, SLOT(updateTimers()));
- connect(d->m_traceWindow, SIGNAL(viewUpdated()), engine, SLOT(dataReceived()));
- connect(this, SIGNAL(connectionFailed()), engine, SLOT(finishProcess()));
- connect(this, SIGNAL(fetchingData(bool)), engine, SLOT(setFetchingData(bool)));
- connect(engine, SIGNAL(starting(const Analyzer::IAnalyzerEngine*)), this, SLOT(setAppIsRunning()));
- connect(engine, SIGNAL(finished()), this, SLOT(setAppIsStopped()));
- connect(this, SIGNAL(cancelRun()), engine, SLOT(finishProcess()));
- connect(engine, SIGNAL(applicationDied()), d->m_traceWindow, SLOT(applicationDied()));
- emit fetchingData(d->m_recordButton->isChecked());
+ connect(engine, SIGNAL(processRunning(quint16)), d->m_profilerConnections, SLOT(connectClient(quint16)));
+ connect(engine, SIGNAL(finished()), d->m_profilerConnections, SLOT(disconnectClient()));
+ connect(d->m_profilerConnections, SIGNAL(connectionFailed()), engine, SLOT(cancelProcess()));
return engine;
}
@@ -392,19 +311,6 @@ bool QmlProfilerTool::canRun(RunConfiguration *runConfiguration, RunMode mode) c
return false;
}
-static QString sysroot(RunConfiguration *runConfig)
-{
- QTC_ASSERT(runConfig, return QString())
- if (Qt4ProjectManager::Qt4BuildConfiguration *buildConfig =
- qobject_cast<Qt4ProjectManager::Qt4BuildConfiguration*>(
- runConfig->target()->activeBuildConfiguration())) {
- if (QtSupport::BaseQtVersion *qtVersion = buildConfig->qtVersion())
- return qtVersion->systemRoot();
- }
-
- return QString();
-}
-
AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration *runConfiguration, RunMode mode) const
{
Q_UNUSED(mode);
@@ -439,7 +345,6 @@ AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration
sp.connParams = rc3->deviceConfig()->sshParameters();
sp.analyzerCmdPrefix = rc3->commandPrefix();
sp.displayName = rc3->displayName();
- sp.sysroot = sysroot(rc3);
} else if (Qt4ProjectManager::S60DeviceRunConfiguration *rc4 =
qobject_cast<Qt4ProjectManager::S60DeviceRunConfiguration *>(runConfiguration)) {
Qt4ProjectManager::S60DeployConfiguration *deployConf =
@@ -449,7 +354,6 @@ AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration
sp.displayName = rc4->displayName();
sp.connParams.host = deployConf->deviceAddress();
sp.connParams.port = rc4->debuggerAspect()->qmlDebugServerPort();
- sp.sysroot = sysroot(rc4);
} else {
// What could that be?
QTC_ASSERT(false, return sp);
@@ -459,53 +363,15 @@ AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration
QWidget *QmlProfilerTool::createWidgets()
{
- QTC_ASSERT(!d->m_traceWindow, return 0);
-
- //
- // DockWidgets
- //
-
- Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
-
- d->m_traceWindow = new TraceWindow(mw);
- d->m_traceWindow->reset(d->m_client);
-
- connect(d->m_traceWindow, SIGNAL(clearViewsFromTool()), this, SLOT(clearDisplay()));
- connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int,int)),this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_traceWindow, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_traceWindow->getEventList(), SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
- connect(d->m_traceWindow->getEventList(), SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- connect(d->m_traceWindow, SIGNAL(profilerStateChanged(bool,bool)), this, SLOT(profilerStateChanged(bool,bool)));
- connect(d->m_traceWindow, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
-
- d->m_eventsView = new QmlProfilerEventsWidget(d->m_traceWindow->getEventList(), mw);
- connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_eventsView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_eventsView, SIGNAL(showEventInTimeline(int)), d->m_traceWindow, SLOT(selectNextEvent(int)));
- connect(d->m_traceWindow, SIGNAL(selectedEventIdChanged(int)), d->m_eventsView, SLOT(updateSelectedEvent(int)));
+ QTC_ASSERT(!d->m_viewContainer, return 0);
- d->m_v8profilerView = new QmlProfilerEventsWidget(d->m_traceWindow->getEventList(), mw);
- d->m_v8profilerView->switchToV8View();
- connect(d->m_v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_v8profilerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), d->m_eventsView, SLOT(selectBySourceLocation(QString,int,int)));
- connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), d->m_v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
-
- QDockWidget *eventsDock = AnalyzerManager::createDockWidget
- (this, tr("Events"), d->m_eventsView, Qt::BottomDockWidgetArea);
- QDockWidget *timelineDock = AnalyzerManager::createDockWidget
- (this, tr("Timeline"), d->m_traceWindow, Qt::BottomDockWidgetArea);
- QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
- (this, tr("JavaScript"), d->m_v8profilerView, Qt::BottomDockWidgetArea);
-
- eventsDock->show();
- timelineDock->show();
- v8profilerDock->show();
-
- mw->splitDockWidget(mw->toolBarDockWidget(), eventsDock, Qt::Vertical);
- mw->tabifyDockWidget(eventsDock, timelineDock);
- mw->tabifyDockWidget(timelineDock, v8profilerDock);
+ d->m_viewContainer = new QmlProfilerViewManager(this,
+ this,
+ d->m_profilerDataModel,
+ d->m_profilerState);
+ connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SLOT(gotoSourceLocation(QString,int,int)));
//
// Toolbar
@@ -522,118 +388,53 @@ QWidget *QmlProfilerTool::createWidgets()
connect(d->m_recordButton,SIGNAL(clicked(bool)), this, SLOT(recordingButtonChanged(bool)));
d->m_recordButton->setChecked(true);
- setRecording(d->m_recordingEnabled);
+ setRecording(d->m_profilerState->clientRecording());
layout->addWidget(d->m_recordButton);
d->m_clearButton = new QToolButton(toolbarWidget);
d->m_clearButton->setIcon(QIcon(QLatin1String(":/qmlprofiler/clean_pane_small.png")));
d->m_clearButton->setToolTip(tr("Discard data"));
- connect(d->m_clearButton,SIGNAL(clicked()), this, SLOT(clearDisplay()));
+
+ connect(d->m_clearButton,SIGNAL(clicked()), this, SLOT(clearData()));
+
layout->addWidget(d->m_clearButton);
- QLabel *timeLabel = new QLabel();
- QPalette palette = timeLabel->palette();
+ d->m_timeLabel = new QLabel();
+ QPalette palette = d->m_timeLabel->palette();
palette.setColor(QPalette::WindowText, Qt::white);
- timeLabel->setPalette(palette);
- timeLabel->setIndent(10);
- connect(d->m_traceWindow, SIGNAL(viewUpdated()), this, SLOT(updateTimers()));
- connect(this, SIGNAL(setTimeLabel(QString)), timeLabel, SLOT(setText(QString)));
- updateTimers();
- layout->addWidget(timeLabel);
+ d->m_timeLabel->setPalette(palette);
+ d->m_timeLabel->setIndent(10);
+ updateTimeDisplay();
+ layout->addWidget(d->m_timeLabel);
toolbarWidget->setLayout(layout);
return toolbarWidget;
}
-void QmlProfilerTool::connectClient(int port)
-{
- if (d->m_client)
- delete d->m_client;
- d->m_client = new QDeclarativeDebugConnection;
- d->m_traceWindow->reset(d->m_client);
- connect(d->m_client, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
- this, SLOT(connectionStateChanged()));
- d->m_connectionTimer.start();
- d->m_appTimer.start();
- d->m_tcpPort = port;
-}
-
-void QmlProfilerTool::connectToClient()
-{
- if (!d->m_client || d->m_client->state() != QAbstractSocket::UnconnectedState)
- return;
-
- if (d->m_connectMode == QmlProfilerToolPrivate::TcpConnection) {
- logStatus(QString("QML Profiler: Connecting to %1:%2...").arg(d->m_tcpHost, QString::number(d->m_tcpPort)));
- d->m_client->connectToHost(d->m_tcpHost, d->m_tcpPort);
- } else {
- logStatus(QString("QML Profiler: Connecting to %1...").arg(d->m_tcpHost));
- d->m_client->connectToOst(d->m_ostDevice);
- }
-}
-
-void QmlProfilerTool::disconnectClient()
-{
- // this might be actually be called indirectly by QDDConnectionPrivate::readyRead(), therefore allow
- // method to complete before deleting object
- if (d->m_client) {
- d->m_client->deleteLater();
- d->m_client = 0;
- }
-}
-
-void QmlProfilerTool::startRecording()
-{
- if (d->m_client && d->m_client->isConnected()) {
- clearDisplay();
- d->m_traceWindow->setRecording(true);
- }
- emit fetchingData(true);
-}
-
-void QmlProfilerTool::stopRecording()
-{
- d->m_traceWindow->setRecording(false);
- emit fetchingData(false);
-
- // manage early stop
- if (d->m_client && !d->m_client->isConnected() && d->m_appIsRunning)
- emit cancelRun();
-}
-
void QmlProfilerTool::recordingButtonChanged(bool recording)
{
- if (recording)
- startRecording();
- else
- stopRecording();
-
- setRecording(recording);
+ d->m_profilerState->setClientRecording(recording);
}
void QmlProfilerTool::setRecording(bool recording)
{
- // update record button
- d->m_recordingEnabled = recording;
+ // update display
d->m_recordButton->setToolTip( recording ? tr("Disable profiling") : tr("Enable profiling"));
d->m_recordButton->setIcon(QIcon(recording ? QLatin1String(":/qmlprofiler/recordOn.png") :
QLatin1String(":/qmlprofiler/recordOff.png")));
d->m_recordButton->setChecked(recording);
- updateTimers();
-}
-void QmlProfilerTool::setAppIsRunning()
-{
- d->m_appIsRunning = true;
- updateTimers();
-}
-
-void QmlProfilerTool::setAppIsStopped()
-{
- d->m_appIsRunning = false;
- updateTimers();
+ // manage timer
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (recording) {
+ d->m_recordingTimer.start();
+ d->m_recordingElapsedTime.start();
+ } else {
+ d->m_recordingTimer.stop();
+ }
+ }
}
void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber)
@@ -658,33 +459,31 @@ void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber,
}
}
-inline QString stringifyTime(double seconds)
+void QmlProfilerTool::updateTimeDisplay()
{
+ double seconds = 0;
+ if (d->m_profilerState->serverRecording() &&
+ d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
+ } else if (d->m_profilerDataModel->currentState() != QmlProfilerDataModel::Empty ) {
+ seconds = (d->m_profilerDataModel->traceEndTime() - d->m_profilerDataModel->traceStartTime()) / 1.0e9;
+ }
QString timeString = QString::number(seconds,'f',1);
- return QmlProfilerTool::tr("%1 s").arg(timeString, 6);
-}
-
-void QmlProfilerTool::updateTimers()
-{
- // prof time
- QString profilerTimeStr = stringifyTime(d->m_traceWindow->profiledTime());
- emit setTimeLabel(tr("Elapsed: %1").arg(profilerTimeStr));
+ QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
+ d->m_timeLabel->setText(tr("Elapsed: %1").arg(profilerTimeStr));
}
-void QmlProfilerTool::profilerStateChanged(bool qmlActive, bool v8active)
+void QmlProfilerTool::clearData()
{
- d->m_v8Active = v8active;
- d->m_qmlActive = qmlActive;
- updateTimers();
+ d->m_profilerDataModel->clear();
+ d->m_profilerConnections->discardPendingData();
}
void QmlProfilerTool::clearDisplay()
{
- d->m_appRunningTime = 0;
- d->m_traceWindow->clearDisplay();
- d->m_eventsView->clear();
- d->m_v8profilerView->clear();
- updateTimers();
+ d->m_profilerConnections->clearBufferedData();
+ d->m_viewContainer->clear();
+ updateTimeDisplay();
}
static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
@@ -733,84 +532,6 @@ static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
ProjectExplorerPlugin::instance()->startRunControl(rc, tool->runMode());
}
-void QmlProfilerTool::tryToConnect()
-{
- ++d->m_connectionAttempts;
-
- if (d->m_client && d->m_client->isConnected()) {
- d->m_connectionTimer.stop();
- d->m_connectionAttempts = 0;
- } else if (d->m_connectionAttempts == 50) {
- d->m_connectionTimer.stop();
- d->m_connectionAttempts = 0;
-
- QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
- infoBox->setIcon(QMessageBox::Critical);
- infoBox->setWindowTitle(tr("Qt Creator"));
- infoBox->setText(tr("Could not connect to the in-process QML profiler.\n"
- "Do you want to retry?"));
- infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel | QMessageBox::Help);
- infoBox->setDefaultButton(QMessageBox::Retry);
- infoBox->setModal(true);
-
- connect(infoBox, SIGNAL(finished(int)),
- this, SLOT(retryMessageBoxFinished(int)));
-
- infoBox->show();
- } else {
- connectToClient();
- }
-}
-
-void QmlProfilerTool::connectionStateChanged()
-{
- if (!d->m_client)
- return;
- switch (d->m_client->state()) {
- case QAbstractSocket::UnconnectedState:
- {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: disconnected");
- break;
- }
- case QAbstractSocket::HostLookupState:
- break;
- case QAbstractSocket::ConnectingState: {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: Connecting to debug server ...");
- break;
- }
- case QAbstractSocket::ConnectedState:
- {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: connected and running");
- updateRecordingState();
- break;
- }
- case QAbstractSocket::ClosingState:
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: closing ...");
- break;
- case QAbstractSocket::BoundState:
- case QAbstractSocket::ListeningState:
- break;
- }
-}
-
-void QmlProfilerTool::updateRecordingState()
-{
- if (d->m_client->isConnected()) {
- d->m_traceWindow->setRecording(d->m_recordingEnabled);
- } else {
- d->m_traceWindow->setRecording(false);
- }
-
- if (d->m_traceWindow->isRecording())
- clearDisplay();
-
- updateTimers();
-}
-
void QmlProfilerTool::startTool(StartMode mode)
{
using namespace ProjectExplorer;
@@ -836,14 +557,25 @@ void QmlProfilerTool::logStatus(const QString &msg)
void QmlProfilerTool::logError(const QString &msg)
{
- // TODO: Rather show errors in the application ouput
MessageManager *messageManager = MessageManager::instance();
messageManager->printToOutputPane(msg, true);
}
+void QmlProfilerTool::showErrorDialog(const QString &error)
+{
+ QMessageBox *errorDialog = new QMessageBox(Core::ICore::mainWindow());
+ errorDialog->setIcon(QMessageBox::Warning);
+ errorDialog->setWindowTitle(tr("QML Profiler"));
+ errorDialog->setText(error);
+ errorDialog->setStandardButtons(QMessageBox::Ok);
+ errorDialog->setDefaultButton(QMessageBox::Ok);
+ errorDialog->setModal(false);
+ errorDialog->show();
+}
+
void QmlProfilerTool::showSaveOption()
{
- d->m_saveQmlTrace->setEnabled(d->m_traceWindow->getEventList()->count());
+ d->m_saveQmlTrace->setEnabled(!d->m_profilerDataModel->isEmpty());
}
void QmlProfilerTool::showSaveDialog()
@@ -852,7 +584,7 @@ void QmlProfilerTool::showSaveDialog()
if (!filename.isEmpty()) {
if (!filename.endsWith(QLatin1String(TraceFileExtension)))
filename += QLatin1String(TraceFileExtension);
- d->m_traceWindow->getEventList()->save(filename);
+ d->m_profilerDataModel->save(filename);
}
}
@@ -868,53 +600,98 @@ void QmlProfilerTool::showLoadDialog()
if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time)
- d->m_traceWindow->getEventList()->setFilename(filename);
- QTimer::singleShot(100, d->m_traceWindow->getEventList(), SLOT(load()));
+ d->m_profilerDataModel->setFilename(filename);
+ QTimer::singleShot(100, d->m_profilerDataModel, SLOT(load()));
}
}
-void QmlProfilerTool::showErrorDialog(const QString &error)
+void QmlProfilerTool::profilerDataModelStateChanged()
{
- QMessageBox *errorDialog = new QMessageBox(Core::ICore::mainWindow());
- errorDialog->setIcon(QMessageBox::Warning);
- errorDialog->setWindowTitle(tr("QML Profiler"));
- errorDialog->setText(error);
- errorDialog->setStandardButtons(QMessageBox::Ok);
- errorDialog->setDefaultButton(QMessageBox::Ok);
- errorDialog->setModal(false);
- errorDialog->show();
+ switch (d->m_profilerDataModel->currentState()) {
+ case QmlProfilerDataModel::Empty :
+ clearDisplay();
+ break;
+ case QmlProfilerDataModel::AcquiringData :
+ case QmlProfilerDataModel::ProcessingData :
+ // nothing to be done for these two
+ break;
+ case QmlProfilerDataModel::Done :
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ showSaveOption();
+ updateTimeDisplay();
+ break;
+ default:
+ break;
+ }
+}
+
+QList <QAction *> QmlProfilerTool::profilerContextMenuActions() const
+{
+ QList <QAction *> commonActions;
+ commonActions << d->m_loadQmlTrace << d->m_saveQmlTrace;
+ return commonActions;
+}
+
+void QmlProfilerTool::showNonmodalWarning(const QString &warningMsg)
+{
+ QMessageBox *noExecWarning = new QMessageBox(Core::ICore::mainWindow());
+ noExecWarning->setIcon(QMessageBox::Warning);
+ noExecWarning->setWindowTitle(tr("QML Profiler"));
+ noExecWarning->setText(warningMsg);
+ noExecWarning->setStandardButtons(QMessageBox::Ok);
+ noExecWarning->setDefaultButton(QMessageBox::Ok);
+ noExecWarning->setModal(false);
+ noExecWarning->show();
+}
+
+QMessageBox *QmlProfilerTool::requestMessageBox()
+{
+ return new QMessageBox(Core::ICore::mainWindow());
}
-void QmlProfilerTool::retryMessageBoxFinished(int result)
+void QmlProfilerTool::handleHelpRequest(const QString &link)
{
- switch (result) {
- case QMessageBox::Retry: {
- d->m_connectionAttempts = 0;
- d->m_connectionTimer.start();
+ HelpManager *helpManager = HelpManager::instance();
+ helpManager->handleHelpRequest(link);
+}
+
+void QmlProfilerTool::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppKilled : {
+ if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData) {
+ showNonmodalWarning(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
+ }
break;
}
- case QMessageBox::Help: {
- HelpManager *helpManager = HelpManager::instance();
- helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html");
- // fall through
- }
- default: {
- if (d->m_client) {
- logStatus("QML Profiler: Failed to connect! " + d->m_client->errorString());
- } else {
- logStatus("QML Profiler: Failed to connect!");
- }
-
- emit connectionFailed();
+ case QmlProfilerStateManager::Idle :
+ // when the app finishes, set recording display to client status
+ setRecording(d->m_profilerState->clientRecording());
+ break;
+ default:
+ // no special action needed for other states
break;
}
+}
+
+void QmlProfilerTool::clientRecordingChanged()
+{
+ // if application is running, display server record changes
+ // if application is stopped, display client record changes
+ if (d->m_profilerState->currentState() != QmlProfilerStateManager::AppRunning) {
+ setRecording(d->m_profilerState->clientRecording());
}
}
-void QmlProfilerTool::eventListStateChanged()
+void QmlProfilerTool::serverRecordingChanged()
{
- if (d->m_traceWindow->getEventList()->currentState() == QmlProfilerEventList::Done) {
- showSaveOption();
- updateTimers();
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ setRecording(d->m_profilerState->serverRecording());
+ // clear the old data each time we start a new profiling session
+ if (d->m_profilerState->serverRecording()) {
+ clearData();
+ }
}
}
+
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 3b53e5d727..fa9ba2e968 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -36,13 +36,13 @@
#include <analyzerbase/ianalyzertool.h>
#include <analyzerbase/ianalyzerengine.h>
-#include <QPoint>
+QT_BEGIN_NAMESPACE
+class QMessageBox;
+QT_END_NAMESPACE
namespace QmlProfiler {
namespace Internal {
-#define TraceFileExtension ".qtd"
-
class QmlProfilerTool : public Analyzer::IAnalyzerTool
{
Q_OBJECT
@@ -72,49 +72,37 @@ public:
QWidget *createWidgets();
void startTool(Analyzer::StartMode mode);
+ QList <QAction *> profilerContextMenuActions() const;
+
+ // display dialogs / log output
+ static QMessageBox *requestMessageBox();
+ static void handleHelpRequest(const QString &link);
+ static void logStatus(const QString &msg);
+ static void logError(const QString &msg);
+ static void showNonmodalWarning(const QString &warningMsg);
+
public slots:
- void connectClient(int port);
- void disconnectClient();
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
- void startRecording();
- void stopRecording();
void recordingButtonChanged(bool recording);
void setRecording(bool recording);
- void setAppIsRunning();
- void setAppIsStopped();
-
void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber);
- void updateTimers();
- void profilerStateChanged(bool qmlActive, bool v8active);
-
- void clearDisplay();
-
- void showContextMenu(const QPoint &position);
-
-signals:
- void setTimeLabel(const QString &);
- void setStatusLabel(const QString &);
- void fetchingData(bool);
- void connectionFailed();
- void cancelRun();
private slots:
- void tryToConnect();
- void connectionStateChanged();
+ void clearData();
+ void showErrorDialog(const QString &error);
+ void profilerDataModelStateChanged();
+ void updateTimeDisplay();
+
void showSaveOption();
void showSaveDialog();
void showLoadDialog();
- void showErrorDialog(const QString &error);
- void retryMessageBoxFinished(int result);
- void eventListStateChanged();
private:
- void connectToClient();
- void updateRecordingState();
- void ensureWidgets();
- void logStatus(const QString &msg);
- void logError(const QString &msg);
+ void clearDisplay();
class QmlProfilerToolPrivate;
QmlProfilerToolPrivate *d;
diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
new file mode 100644
index 0000000000..ab02f45b08
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
@@ -0,0 +1,601 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilerdatamodel.h"
+
+// Needed for the load&save actions in the context menu
+#include <analyzerbase/ianalyzertool.h>
+
+// Comunication with the other views (limit events to range)
+#include "qmlprofilerviewmanager.h"
+
+#include <utils/styledbar.h>
+
+#include <QDeclarativeContext>
+#include <QToolButton>
+#include <QEvent>
+#include <QVBoxLayout>
+#include <QGraphicsObject>
+#include <QScrollBar>
+#include <QSlider>
+#include <QMenu>
+
+#include <math.h>
+
+using namespace QmlJsDebugClient;
+
+namespace QmlProfiler {
+namespace Internal {
+
+const int sliderTicks = 10000;
+const qreal sliderExp = 3;
+
+
+/////////////////////////////////////////////////////////
+bool MouseWheelResizer::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::Wheel) {
+ QWheelEvent *ev = static_cast<QWheelEvent *>(event);
+ if (ev->modifiers() & Qt::ControlModifier) {
+ emit mouseWheelMoved(ev->pos().x(), ev->pos().y(), ev->delta());
+ return true;
+ }
+ }
+ return QObject::eventFilter(obj, event);
+}
+
+/////////////////////////////////////////////////////////
+void ZoomControl::setRange(qint64 startTime, qint64 endTime)
+{
+ if (m_startTime != startTime || m_endTime != endTime) {
+ m_startTime = startTime;
+ m_endTime = endTime;
+ emit rangeChanged();
+ }
+}
+
+/////////////////////////////////////////////////////////
+ScrollableDeclarativeView::ScrollableDeclarativeView(QWidget *parent)
+ : QDeclarativeView(parent)
+{
+}
+
+ScrollableDeclarativeView::~ScrollableDeclarativeView()
+{
+}
+
+void ScrollableDeclarativeView::scrollContentsBy(int dx, int dy)
+{
+ // special workaround to track the scrollbar
+ if (rootObject()) {
+ int scrollY = rootObject()->property("scrollY").toInt();
+ rootObject()->setProperty("scrollY", QVariant(scrollY - dy));
+ }
+ QDeclarativeView::scrollContentsBy(dx,dy);
+}
+
+/////////////////////////////////////////////////////////
+class QmlProfilerTraceView::QmlProfilerTraceViewPrivate
+{
+public:
+ QmlProfilerTraceViewPrivate(QmlProfilerTraceView *qq) : q(qq) {}
+ QmlProfilerTraceView *q;
+
+ QmlProfilerStateManager *m_profilerState;
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QSize m_sizeHint;
+
+ ScrollableDeclarativeView *m_mainView;
+ QDeclarativeView *m_timebar;
+ QDeclarativeView *m_overview;
+ QmlProfilerDataModel *m_profilerDataModel;
+
+ ZoomControl *m_zoomControl;
+
+ QToolButton *m_buttonRange;
+ QToolButton *m_buttonLock;
+ QWidget *m_zoomToolbar;
+ int m_currentZoomLevel;
+};
+
+QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState)
+ : QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
+{
+ setObjectName("QML Profiler");
+
+ d->m_zoomControl = new ZoomControl(this);
+ connect(d->m_zoomControl, SIGNAL(rangeChanged()), this, SLOT(updateRange()));
+
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0, 0, 0, 0);
+ groupLayout->setSpacing(0);
+
+ d->m_mainView = new ScrollableDeclarativeView(this);
+ d->m_mainView->setResizeMode(QDeclarativeView::SizeViewToRootObject);
+ d->m_mainView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ d->m_mainView->setBackgroundBrush(QBrush(Qt::white));
+ d->m_mainView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ d->m_mainView->setFocus();
+
+ MouseWheelResizer *resizer = new MouseWheelResizer(this);
+ connect(resizer,SIGNAL(mouseWheelMoved(int,int,int)), this, SLOT(mouseWheelMoved(int,int,int)));
+ d->m_mainView->viewport()->installEventFilter(resizer);
+
+ QHBoxLayout *toolsLayout = new QHBoxLayout;
+
+ d->m_timebar = new QDeclarativeView(this);
+ d->m_timebar->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_timebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_timebar->setFixedHeight(24);
+
+ d->m_overview = new QDeclarativeView(this);
+ d->m_overview->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_overview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_overview->setMaximumHeight(50);
+
+ d->m_zoomToolbar = createZoomToolbar();
+ d->m_zoomToolbar->move(0, d->m_timebar->height());
+ d->m_zoomToolbar->setVisible(false);
+
+ toolsLayout->addWidget(createToolbar());
+ toolsLayout->addWidget(d->m_timebar);
+ emit enableToolbar(false);
+
+ groupLayout->addLayout(toolsLayout);
+ groupLayout->addWidget(d->m_mainView);
+ groupLayout->addWidget(d->m_overview);
+
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_profilerDataModel = model;
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+ d->m_mainView->rootContext()->setContextProperty("qmlProfilerDataModel",
+ d->m_profilerDataModel);
+ d->m_overview->rootContext()->setContextProperty("qmlProfilerDataModel",
+ d->m_profilerDataModel);
+
+ d->m_profilerState = profilerState;
+ connect(d->m_profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+
+ // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
+ setMinimumHeight(170);
+ d->m_currentZoomLevel = 0;
+}
+
+QmlProfilerTraceView::~QmlProfilerTraceView()
+{
+ delete d;
+}
+
+/////////////////////////////////////////////////////////
+// Initialize widgets
+void QmlProfilerTraceView::reset()
+{
+ d->m_mainView->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+ d->m_timebar->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+ d->m_overview->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+
+ d->m_timebar->setSource(QUrl("qrc:/qmlprofiler/TimeDisplay.qml"));
+ d->m_overview->setSource(QUrl("qrc:/qmlprofiler/Overview.qml"));
+
+ d->m_mainView->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
+ d->m_mainView->rootObject()->setProperty("width", QVariant(width()));
+ d->m_mainView->rootObject()->setProperty("candidateHeight", QVariant(height() - d->m_timebar->height() - d->m_overview->height()));
+
+ connect(d->m_mainView->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateLockButton()), this, SLOT(updateLockButton()));
+ connect(this, SIGNAL(jumpToPrev()), d->m_mainView->rootObject(), SLOT(prevEvent()));
+ connect(this, SIGNAL(jumpToNext()), d->m_mainView->rootObject(), SLOT(nextEvent()));
+ connect(d->m_mainView->rootObject(), SIGNAL(selectedEventChanged(int)), this, SIGNAL(selectedEventChanged(int)));
+ connect(d->m_mainView->rootObject(), SIGNAL(changeToolTip(QString)), this, SLOT(updateToolTip(QString)));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateVerticalScroll(int)), this, SLOT(updateVerticalScroll(int)));
+}
+
+QWidget *QmlProfilerTraceView::createToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QToolButton *buttonPrev= new QToolButton;
+ buttonPrev->setIcon(QIcon(":/qmlprofiler/ico_prev.png"));
+ buttonPrev->setToolTip(tr("Jump to previous event"));
+ connect(buttonPrev, SIGNAL(clicked()), this, SIGNAL(jumpToPrev()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonPrev, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonNext= new QToolButton;
+ buttonNext->setIcon(QIcon(":/qmlprofiler/ico_next.png"));
+ buttonNext->setToolTip(tr("Jump to next event"));
+ connect(buttonNext, SIGNAL(clicked()), this, SIGNAL(jumpToNext()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonNext, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonZoomControls = new QToolButton;
+ buttonZoomControls->setIcon(QIcon(":/qmlprofiler/ico_zoom.png"));
+ buttonZoomControls->setToolTip(tr("Show zoom slider"));
+ buttonZoomControls->setCheckable(true);
+ buttonZoomControls->setChecked(false);
+ connect(buttonZoomControls, SIGNAL(toggled(bool)), d->m_zoomToolbar, SLOT(setVisible(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonZoomControls, SLOT(setEnabled(bool)));
+
+ d->m_buttonRange = new QToolButton;
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ d->m_buttonRange->setToolTip(tr("Select range"));
+ d->m_buttonRange->setCheckable(true);
+ d->m_buttonRange->setChecked(false);
+ connect(d->m_buttonRange, SIGNAL(clicked(bool)), this, SLOT(toggleRangeMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonRange, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(rangeModeChanged(bool)), d->m_buttonRange, SLOT(setChecked(bool)));
+
+ d->m_buttonLock = new QToolButton;
+ d->m_buttonLock->setIcon(QIcon(":/qmlprofiler/ico_selectionmode.png"));
+ d->m_buttonLock->setToolTip(tr("View event information on mouseover"));
+ d->m_buttonLock->setCheckable(true);
+ d->m_buttonLock->setChecked(false);
+ connect(d->m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonLock, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(lockModeChanged(bool)), d->m_buttonLock, SLOT(setChecked(bool)));
+
+ toolBarLayout->addWidget(buttonPrev);
+ toolBarLayout->addWidget(buttonNext);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(buttonZoomControls);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(d->m_buttonRange);
+ toolBarLayout->addWidget(d->m_buttonLock);
+
+ return bar;
+}
+
+
+QWidget *QmlProfilerTraceView::createZoomToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QSlider *zoomSlider = new QSlider(Qt::Horizontal);
+ zoomSlider->setFocusPolicy(Qt::NoFocus);
+ zoomSlider->setRange(1, sliderTicks);
+ zoomSlider->setInvertedAppearance(true);
+ zoomSlider->setPageStep(sliderTicks/100);
+ connect(this, SIGNAL(enableToolbar(bool)), zoomSlider, SLOT(setEnabled(bool)));
+ connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
+ connect(this, SIGNAL(zoomLevelChanged(int)), zoomSlider, SLOT(setValue(int)));
+ zoomSlider->setStyleSheet("\
+ QSlider:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #5a5a5a);\
+ border: 1px #313131;\
+ height: 20px;\
+ margin: 0px 0px 0px 0px;\
+ }\
+ QSlider::add-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ QSlider::sub-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ ");
+
+ toolBarLayout->addWidget(zoomSlider);
+
+ return bar;
+}
+
+/////////////////////////////////////////////////////////
+bool QmlProfilerTraceView::hasValidSelection() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeReady").toBool();
+ }
+ return false;
+}
+
+qint64 QmlProfilerTraceView::selectionStart() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeStart").toLongLong();
+ }
+ return 0;
+}
+
+qint64 QmlProfilerTraceView::selectionEnd() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeEnd").toLongLong();
+ }
+ return 0;
+}
+
+void QmlProfilerTraceView::clearDisplay()
+{
+ d->m_zoomControl->setRange(0,0);
+
+ updateVerticalScroll(0);
+ d->m_mainView->rootObject()->setProperty("scrollY", QVariant(0));
+
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "clearAll");
+ QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay");
+}
+
+void QmlProfilerTraceView::selectNextEventWithId(int eventId)
+{
+ if (d->m_mainView->rootObject())
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "selectNextWithId",
+ Q_ARG(QVariant,QVariant(eventId)));
+}
+
+/////////////////////////////////////////////////////////
+// Goto source location
+void QmlProfilerTraceView::updateCursorPosition()
+{
+ emit gotoSourceLocation(d->m_mainView->rootObject()->property("fileName").toString(),
+ d->m_mainView->rootObject()->property("lineNumber").toInt(),
+ d->m_mainView->rootObject()->property("columnNumber").toInt());
+}
+
+/////////////////////////////////////////////////////////
+// Toolbar buttons
+void QmlProfilerTraceView::toggleRangeMode(bool active)
+{
+ bool rangeMode = d->m_mainView->rootObject()->property("selectionRangeMode").toBool();
+ if (active != rangeMode) {
+ if (active)
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
+ else
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ d->m_mainView->rootObject()->setProperty("selectionRangeMode", QVariant(active));
+ }
+}
+
+void QmlProfilerTraceView::updateRangeButton()
+{
+ bool rangeMode = d->m_mainView->rootObject()->property("selectionRangeMode").toBool();
+ if (rangeMode)
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
+ else
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ emit rangeModeChanged(rangeMode);
+}
+
+void QmlProfilerTraceView::toggleLockMode(bool active)
+{
+ bool lockMode = !d->m_mainView->rootObject()->property("selectionLocked").toBool();
+ if (active != lockMode) {
+ d->m_mainView->rootObject()->setProperty("selectionLocked", QVariant(!active));
+ d->m_mainView->rootObject()->setProperty("selectedItem", QVariant(-1));
+ }
+}
+
+void QmlProfilerTraceView::updateLockButton()
+{
+ bool lockMode = !d->m_mainView->rootObject()->property("selectionLocked").toBool();
+ emit lockModeChanged(lockMode);
+}
+
+////////////////////////////////////////////////////////
+// Zoom control
+void QmlProfilerTraceView::setZoomLevel(int zoomLevel)
+{
+ if (d->m_currentZoomLevel != zoomLevel && d->m_mainView->rootObject()) {
+ QVariant newFactor = pow(qreal(zoomLevel) / qreal(sliderTicks), sliderExp);
+ d->m_currentZoomLevel = zoomLevel;
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "updateWindowLength", Q_ARG(QVariant, newFactor));
+ }
+}
+
+void QmlProfilerTraceView::updateRange()
+{
+ if (!d->m_profilerDataModel)
+ return;
+ qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime();
+ if (duration <= 0)
+ return;
+ if (d->m_profilerDataModel->traceDuration() <= 0)
+ return;
+ int newLevel = pow(duration / d->m_profilerDataModel->traceDuration(), 1/sliderExp) * sliderTicks;
+ if (d->m_currentZoomLevel != newLevel) {
+ d->m_currentZoomLevel = newLevel;
+ emit zoomLevelChanged(newLevel);
+ }
+}
+
+void QmlProfilerTraceView::mouseWheelMoved(int mouseX, int mouseY, int wheelDelta)
+{
+ Q_UNUSED(mouseY);
+ if (d->m_mainView->rootObject()) {
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "wheelZoom",
+ Q_ARG(QVariant, QVariant(mouseX)),
+ Q_ARG(QVariant, QVariant(wheelDelta)));
+ }
+}
+////////////////////////////////////////////////////////
+void QmlProfilerTraceView::updateToolTip(const QString &text)
+{
+ setToolTip(text);
+}
+
+void QmlProfilerTraceView::updateVerticalScroll(int newPosition)
+{
+ d->m_mainView->verticalScrollBar()->setValue(newPosition);
+}
+
+void QmlProfilerTraceView::resizeEvent(QResizeEvent *event)
+{
+ if (d->m_mainView->rootObject()) {
+ d->m_mainView->rootObject()->setProperty("width", QVariant(event->size().width()));
+ int newHeight = event->size().height() - d->m_timebar->height() - d->m_overview->height();
+ d->m_mainView->rootObject()->setProperty("candidateHeight", QVariant(newHeight));
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// Context menu
+void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *viewAllAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (d->m_viewContainer->hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+
+ if (d->m_profilerDataModel->count() > 0) {
+ menu.addSeparator();
+ viewAllAction = menu.addAction(tr("Reset Zoom"));
+ }
+
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == viewAllAction) {
+ d->m_zoomControl->setRange(
+ d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ if (selectedAction == getLocalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(
+ d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(
+ d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+// Tell QML the state of the profiler
+void QmlProfilerTraceView::setRecording(bool recording)
+{
+ if (d->m_mainView->rootObject())
+ d->m_mainView->rootObject()->setProperty("recordingEnabled", QVariant(recording));
+}
+
+void QmlProfilerTraceView::setAppKilled()
+{
+ if (d->m_mainView->rootObject())
+ d->m_mainView->rootObject()->setProperty("appKilled",QVariant(true));
+}
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerTraceView::profilerDataModelStateChanged()
+{
+ switch (d->m_profilerDataModel->currentState()) {
+ case QmlProfilerDataModel::Empty :
+ emit enableToolbar(false);
+ break;
+ case QmlProfilerDataModel::AcquiringData :
+ // nothing to be done
+ break;
+ case QmlProfilerDataModel::ProcessingData :
+ // nothing to be done
+ break;
+ case QmlProfilerDataModel::Done :
+ emit enableToolbar(true);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerTraceView::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppKilled : {
+ if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData)
+ setAppKilled();
+ break;
+ }
+ default:
+ // no special action needed for other states
+ break;
+ }
+}
+
+void QmlProfilerTraceView::clientRecordingChanged()
+{
+ // nothing yet
+}
+
+void QmlProfilerTraceView::serverRecordingChanged()
+{
+ setRecording(d->m_profilerState->serverRecording());
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/qmlprofilertraceview.h
index 2c653e5c0a..6b61380882 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.h
@@ -30,24 +30,23 @@
**
**************************************************************************/
-#ifndef TRACEWINDOW_H
-#define TRACEWINDOW_H
+#ifndef QMLPROFILERTRACEVIEW_H
+#define QMLPROFILERTRACEVIEW_H
-#include <qmljsdebugclient/qmlprofilertraceclient.h>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include "qmlprofilerdetailsrewriter.h"
-#include <qmljsdebugclient/qv8profilerclient.h>
-
-#include <QPointer>
-#include <QWidget>
-#include <QToolButton>
-
-#include <QEvent>
#include <QDeclarativeView>
+namespace Analyzer {
+class IAnalyzerTool;
+}
+
namespace QmlProfiler {
namespace Internal {
+class QmlProfilerStateManager;
+class QmlProfilerViewManager;
+class QmlProfilerDataModel;
+
+// capture mouse wheel events
class MouseWheelResizer : public QObject {
Q_OBJECT
public:
@@ -87,118 +86,73 @@ protected:
void scrollContentsBy(int dx, int dy);
};
-class TraceWindow : public QWidget
+class QmlProfilerTraceView : public QWidget
{
Q_OBJECT
public:
- TraceWindow(QWidget *parent = 0);
- ~TraceWindow();
-
- void reset(QmlJsDebugClient::QDeclarativeDebugConnection *conn);
+ explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState);
+ ~QmlProfilerTraceView();
- QmlJsDebugClient::QmlProfilerEventList *getEventList() const;
- ZoomControl *rangeTimes() const;
-
- void setRecording(bool recording);
- bool isRecording() const;
- void viewAll();
+ void reset();
bool hasValidSelection() const;
qint64 selectionStart() const;
qint64 selectionEnd() const;
- double profiledTime() const;
public slots:
void clearDisplay();
- void selectNextEvent(int eventId);
- void applicationDied();
+ void selectNextEventWithId(int eventId);
private slots:
void updateCursorPosition();
- void updateTimer();
- void updateToolbar();
void toggleRangeMode(bool);
- void toggleLockMode(bool);
void updateRangeButton();
+ void toggleLockMode(bool);
void updateLockButton();
+
void setZoomLevel(int zoomLevel);
void updateRange();
- void mouseWheelMoved(int x, int y, int delta);
+ void mouseWheelMoved(int mouseX, int mouseY, int wheelDelta);
- void qmlComplete();
- void v8Complete();
- void updateProfilerState();
void updateToolTip(const QString &text);
void updateVerticalScroll(int newPosition);
- void eventListStateChanged();
- void manageTraceStart(qint64 traceStart);
- void firstDataReceived();
- void correctTimer();
+ void profilerDataModelStateChanged();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *event);
+
+private slots:
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
signals:
- void viewUpdated();
- void profilerStateChanged(bool qmlActive, bool v8active);
void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columNumber);
- void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location);
- void v8range(int depth,const QString &function,const QString &filename,
- int lineNumber, double totalTime, double selfTime);
- void traceFinished(qint64);
- void traceStarted(qint64);
- void frameEvent(qint64, int, int);
- void recordingChanged(bool);
-
- void internalClearDisplay();
- void clearViewsFromTool();
+ void selectedEventChanged(int eventId);
+
void jumpToPrev();
void jumpToNext();
void rangeModeChanged(bool);
void lockModeChanged(bool);
void enableToolbar(bool);
void zoomLevelChanged(int);
- void updateViewZoom(QVariant zoomLevel);
- void wheelZoom(QVariant wheelCenter, QVariant wheelDelta);
- void globalZoom();
-
- void contextMenuRequested(const QPoint& position);
- void selectNextEventInDisplay(QVariant eventId);
- void selectedEventIdChanged(int eventId);
private:
void contextMenuEvent(QContextMenuEvent *);
QWidget *createToolbar();
QWidget *createZoomToolbar();
- void connectClientSignals();
- void disconnectClientSignals();
- void initializeQmlViews();
-protected:
- virtual void resizeEvent(QResizeEvent *event);
+ void setRecording(bool recording);
+ void setAppKilled();
private:
- QWeakPointer<QmlJsDebugClient::QmlProfilerTraceClient> m_plugin;
- QWeakPointer<QmlJsDebugClient::QV8ProfilerClient> m_v8plugin;
- QSize m_sizeHint;
-
- ScrollableDeclarativeView *m_mainView;
- QDeclarativeView *m_timebar;
- QDeclarativeView *m_overview;
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
- QmlProfilerDetailsRewriter *m_rewriter;
- bool m_qmlDataReady;
- bool m_v8DataReady;
- double m_profiledTime;
-
- QWeakPointer<ZoomControl> m_zoomControl;
-
- QToolButton *m_buttonRange;
- QToolButton *m_buttonLock;
- QWidget *m_zoomToolbar;
- int m_currentZoomLevel;
+ class QmlProfilerTraceViewPrivate;
+ QmlProfilerTraceViewPrivate *d;
};
} // namespace Internal
} // namespace QmlProfiler
-#endif // TRACEWINDOW_H
+#endif // QMLPROFILERTRACEVIEW_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
new file mode 100644
index 0000000000..a12a6ccda5
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
@@ -0,0 +1,172 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerviewmanager.h"
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilereventview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilerdatamodel.h"
+
+#include <utils/qtcassert.h>
+#include <utils/fancymainwindow.h>
+#include <analyzerbase/analyzermanager.h>
+
+
+#include <QDockWidget>
+
+using namespace Analyzer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerViewManager::QmlProfilerViewManagerPrivate {
+public:
+ QmlProfilerViewManagerPrivate(QmlProfilerViewManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerTraceView *traceView;
+ QmlProfilerEventsWidget *eventsView;
+ QmlProfilerEventsWidget *v8profilerView;
+ QmlProfilerStateManager *profilerState;
+ QmlProfilerDataModel *profilerDataModel;
+ QmlProfilerTool *profilerTool;
+};
+
+QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerDataModel *model,
+ QmlProfilerStateManager *profilerState)
+ : QObject(parent), d(new QmlProfilerViewManagerPrivate(this))
+{
+ setObjectName("QML Profiler View Manager");
+ d->traceView = 0;
+ d->eventsView = 0;
+ d->v8profilerView = 0;
+ d->profilerState = profilerState;
+ d->profilerDataModel = model;
+ d->profilerTool = profilerTool;
+ createViews();
+}
+
+QmlProfilerViewManager::~QmlProfilerViewManager()
+{
+ delete d;
+}
+
+////////////////////////////////////////////////////////////
+// Views
+void QmlProfilerViewManager::createViews()
+{
+ QTC_ASSERT(d->profilerDataModel, return);
+ QTC_ASSERT(d->profilerState, return);
+
+ Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
+
+ d->traceView = new QmlProfilerTraceView(mw,
+ d->profilerTool,
+ this,
+ d->profilerDataModel,
+ d->profilerState);
+ connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+
+ d->traceView->reset();
+
+ d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this, d->profilerDataModel);
+ connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this,
+ SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->eventsView, SIGNAL(showEventInTimeline(int)), d->traceView,
+ SLOT(selectNextEventWithId(int)));
+ connect(d->traceView, SIGNAL(selectedEventChanged(int)), d->eventsView,
+ SLOT(updateSelectedEvent(int)));
+
+ d->v8profilerView = new QmlProfilerEventsWidget(mw, d->profilerTool,
+ this, d->profilerDataModel);
+ d->v8profilerView->switchToV8View();
+ connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
+ connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
+
+ QDockWidget *eventsDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea);
+ QDockWidget *timelineDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea);
+ QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
+
+ eventsDock->show();
+ timelineDock->show();
+ v8profilerDock->show();
+
+ mw->splitDockWidget(mw->toolBarDockWidget(), eventsDock, Qt::Vertical);
+ mw->tabifyDockWidget(eventsDock, timelineDock);
+ mw->tabifyDockWidget(timelineDock, v8profilerDock);
+}
+
+bool QmlProfilerViewManager::hasValidSelection() const
+{
+ return d->traceView->hasValidSelection();
+}
+
+qint64 QmlProfilerViewManager::selectionStart() const
+{
+ return d->traceView->selectionStart();
+}
+
+qint64 QmlProfilerViewManager::selectionEnd() const
+{
+ return d->traceView->selectionEnd();
+}
+
+bool QmlProfilerViewManager::hasGlobalStats() const
+{
+ return d->eventsView->hasGlobalStats();
+}
+
+void QmlProfilerViewManager::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->eventsView->getStatisticsInRange(rangeStart, rangeEnd);
+}
+
+void QmlProfilerViewManager::clear()
+{
+ d->traceView->clearDisplay();
+ d->eventsView->clear();
+ d->v8profilerView->clear();
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.h b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
new file mode 100644
index 0000000000..4e492918ee
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
@@ -0,0 +1,79 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILERVIEWMANAGER_H
+#define QMLPROFILERVIEWMANAGER_H
+
+#include <QObject>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTool;
+class QmlProfilerDataModel;
+class QmlProfilerStateManager;
+
+class QmlProfilerViewManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerDataModel *model,
+ QmlProfilerStateManager *profilerState);
+ ~QmlProfilerViewManager();
+
+ void createViews();
+
+ // used by the options "limit events to range"
+ bool hasValidSelection() const;
+ qint64 selectionStart() const;
+ qint64 selectionEnd() const;
+ bool hasGlobalStats() const;
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+
+public slots:
+ void clear();
+
+signals:
+ void gotoSourceLocation(QString,int,int);
+
+private:
+ class QmlProfilerViewManagerPrivate;
+ QmlProfilerViewManagerPrivate *d;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERVIEWMANAGER_H
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
new file mode 100644
index 0000000000..d58f198be6
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
@@ -0,0 +1,445 @@
+#include "qv8profilerdatamodel.h"
+#include "qmlprofilerdatamodel.h"
+
+#include <QStringList>
+
+using namespace QmlJsDebugClient;
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+namespace QmlProfiler {
+namespace Internal {
+
+QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
+{
+ if (this == &ref)
+ return *this;
+
+ displayName = ref.displayName;
+ eventHashStr = ref.eventHashStr;
+ filename = ref.filename;
+ functionName = ref.functionName;
+ line = ref.line;
+ totalTime = ref.totalTime;
+ totalPercent = ref.totalPercent;
+ selfTime = ref.selfTime;
+ selfPercent = ref.selfPercent;
+ eventId = ref.eventId;
+
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ foreach (const QString &key, ref.parentHash.keys()) {
+ parentHash.insert(key, new QV8EventSub(ref.parentHash.value(key)));
+ }
+
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+ foreach (const QString &key, ref.childrenHash.keys()) {
+ childrenHash.insert(key, new QV8EventSub(ref.childrenHash.value(key)));
+ }
+ return *this;
+}
+
+QV8EventData::QV8EventData()
+{
+ line = -1;
+ eventId = -1;
+ totalTime = 0;
+ selfTime = 0;
+ totalPercent = 0;
+ selfPercent = 0;
+}
+
+QV8EventData::~QV8EventData()
+{
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+}
+
+class QV8ProfilerDataModel::QV8ProfilerDataModelPrivate
+{
+public:
+ QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);}
+
+ QmlProfilerDataModel *qmlProfilerDataModel;
+
+ void clearV8RootEvent();
+ void collectV8Statistics();
+
+ QHash<QString, QV8EventData *> v8EventHash;
+ QHash<int, QV8EventData *> v8parents;
+ QV8EventData v8RootEvent;
+ qint64 v8MeasuredTime;
+};
+
+QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent,
+ QmlProfilerDataModel *profilerDataModel)
+ : QObject(parent), d(new QV8ProfilerDataModelPrivate(this))
+{
+ d->v8MeasuredTime = 0;
+ d->clearV8RootEvent();
+ d->qmlProfilerDataModel = profilerDataModel;
+}
+
+QV8ProfilerDataModel::~QV8ProfilerDataModel()
+{
+ delete d;
+}
+
+void QV8ProfilerDataModel::clear()
+{
+ qDeleteAll(d->v8EventHash.values());
+ d->v8EventHash.clear();
+ d->v8parents.clear();
+ d->clearV8RootEvent();
+ d->v8MeasuredTime = 0;
+}
+
+bool QV8ProfilerDataModel::isEmpty() const
+{
+ return d->v8EventHash.isEmpty();
+}
+
+QV8EventData *QV8ProfilerDataModel::v8EventDescription(int eventId) const
+{
+ foreach (QV8EventData *event, d->v8EventHash.values()) {
+ if (event->eventId == eventId)
+ return event;
+ }
+ return 0;
+}
+
+qint64 QV8ProfilerDataModel::v8MeasuredTime() const
+{
+ return d->v8MeasuredTime;
+}
+
+QList<QV8EventData *> QV8ProfilerDataModel::getV8Events() const
+{
+ return d->v8EventHash.values();
+}
+
+void QV8ProfilerDataModel::addV8Event(int depth,
+ const QString &function,
+ const QString &filename,
+ int lineNumber,
+ double totalTime,
+ double selfTime)
+{
+ QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) +
+ QLatin1Char(':') + QString::number(lineNumber);
+ QString hashStr = QmlProfilerDataModel::getHashStringForV8Event(displayName, function);
+
+ d->qmlProfilerDataModel->setState(QmlProfilerDataModel::AcquiringData);
+
+ // time is given in milliseconds, but internally we store it in microseconds
+ totalTime *= 1e6;
+ selfTime *= 1e6;
+
+ // accumulate information
+ QV8EventData *eventData = d->v8EventHash[hashStr];
+ if (!eventData) {
+ eventData = new QV8EventData;
+ eventData->displayName = displayName;
+ eventData->eventHashStr = hashStr;
+ eventData->filename = filename;
+ eventData->functionName = function;
+ eventData->line = lineNumber;
+ eventData->totalTime = totalTime;
+ eventData->selfTime = selfTime;
+ d->v8EventHash[hashStr] = eventData;
+ } else {
+ eventData->totalTime += totalTime;
+ eventData->selfTime += selfTime;
+ }
+ d->v8parents[depth] = eventData;
+
+ QV8EventData *parentEvent = 0;
+ if (depth == 0) {
+ parentEvent = &d->v8RootEvent;
+ d->v8MeasuredTime += totalTime;
+ }
+ if (depth > 0 && d->v8parents.contains(depth-1)) {
+ parentEvent = d->v8parents.value(depth-1);
+ }
+
+ if (parentEvent != 0) {
+ if (!eventData->parentHash.contains(parentEvent->eventHashStr)) {
+ QV8EventSub *newParentSub = new QV8EventSub(parentEvent);
+ newParentSub->totalTime = totalTime;
+
+ eventData->parentHash.insert(parentEvent->eventHashStr, newParentSub);
+ } else {
+ QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->eventHashStr);
+ newParentSub->totalTime += totalTime;
+ }
+
+ if (!parentEvent->childrenHash.contains(eventData->eventHashStr)) {
+ QV8EventSub *newChildSub = new QV8EventSub(eventData);
+ newChildSub->totalTime = totalTime;
+
+ parentEvent->childrenHash.insert(eventData->eventHashStr, newChildSub);
+ } else {
+ QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->eventHashStr);
+ newChildSub->totalTime += totalTime;
+ }
+ }
+}
+
+void QV8ProfilerDataModel::collectV8Statistics()
+{
+ d->collectV8Statistics();
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
+{
+ if (!v8EventHash.isEmpty()) {
+ double totalTimes = v8MeasuredTime;
+ double selfTimes = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ selfTimes += v8event->selfTime;
+ }
+
+ // prevent divisions by 0
+ if (totalTimes == 0)
+ totalTimes = 1;
+ if (selfTimes == 0)
+ selfTimes = 1;
+
+ // insert root event in eventlist
+ // the +1 ns is to get it on top of the sorted list
+ v8RootEvent.totalTime = v8MeasuredTime + 1;
+ v8RootEvent.selfTime = 0;
+
+ QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event(
+ QmlProfilerDataModel::rootEventName(),
+ QmlProfilerDataModel::rootEventDescription());
+ QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash];
+ if (v8RootEventPointer) {
+ v8RootEvent = *v8RootEventPointer;
+ } else {
+ v8EventHash[rootEventHash] = new QV8EventData;
+ *v8EventHash[rootEventHash] = v8RootEvent;
+ }
+
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
+ v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
+ }
+
+ int index = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->eventId = index++;
+ }
+ v8RootEvent.eventId = v8EventHash[rootEventHash]->eventId;
+ }
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent()
+{
+ v8RootEvent.displayName = QmlProfilerDataModel::rootEventName();
+ v8RootEvent.eventHashStr = QmlProfilerDataModel::rootEventName();
+ v8RootEvent.functionName = QmlProfilerDataModel::rootEventDescription();
+ v8RootEvent.line = -1;
+ v8RootEvent.totalTime = 0;
+ v8RootEvent.totalPercent = 0;
+ v8RootEvent.selfTime = 0;
+ v8RootEvent.selfPercent = 0;
+ v8RootEvent.eventId = -1;
+
+ qDeleteAll(v8RootEvent.parentHash.values());
+ qDeleteAll(v8RootEvent.childrenHash.values());
+ v8RootEvent.parentHash.clear();
+ v8RootEvent.childrenHash.clear();
+}
+
+void QV8ProfilerDataModel::save(QXmlStreamWriter &stream)
+{
+ stream.writeStartElement("v8profile"); // v8 profiler output
+ stream.writeAttribute("totalTime", QString::number(d->v8MeasuredTime));
+ foreach (QV8EventData *v8event, d->v8EventHash.values()) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index",
+ QString::number(
+ d->v8EventHash.keys().indexOf(
+ v8event->eventHashStr)));
+ stream.writeTextElement("displayname", v8event->displayName);
+ stream.writeTextElement("functionname", v8event->functionName);
+ if (!v8event->filename.isEmpty()) {
+ stream.writeTextElement("filename", v8event->filename);
+ stream.writeTextElement("line", QString::number(v8event->line));
+ }
+ stream.writeTextElement("totalTime", QString::number(v8event->totalTime));
+ stream.writeTextElement("selfTime", QString::number(v8event->selfTime));
+ if (!v8event->childrenHash.isEmpty()) {
+ stream.writeStartElement("childrenEvents");
+ QStringList childrenIndexes;
+ QStringList childrenTimes;
+ QStringList parentTimes;
+ foreach (QV8EventSub *v8child, v8event->childrenHash.values()) {
+ childrenIndexes << QString::number(v8child->reference->eventId);
+ childrenTimes << QString::number(v8child->totalTime);
+ parentTimes << QString::number(v8child->totalTime);
+ }
+
+ stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
+ stream.writeAttribute("childrenTimes", childrenTimes.join(QString(", ")));
+ stream.writeAttribute("parentTimes", parentTimes.join(QString(", ")));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // v8 profiler output
+}
+
+void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
+{
+ QHash <int, QV8EventData *> v8eventBuffer;
+ QHash <int, QString> childrenIndexes;
+ QHash <int, QString> childrenTimes;
+ QHash <int, QString> parentTimes;
+ QV8EventData *v8event = 0;
+
+ // time computation
+ d->v8MeasuredTime = 0;
+ double cumulatedV8Time = 0;
+
+ // get the v8 time
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("totalTime"))
+ d->v8MeasuredTime = attributes.value("totalTime").toString().toDouble();
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ QString elementName = stream.name().toString();
+ switch (token) {
+ case QXmlStreamReader::StartDocument : continue;
+ case QXmlStreamReader::StartElement : {
+ if (elementName == "event") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("index")) {
+ int ndx = attributes.value("index").toString().toInt();
+ if (!v8eventBuffer.value(ndx))
+ v8eventBuffer[ndx] = new QV8EventData;
+ v8event = v8eventBuffer[ndx];
+ } else {
+ v8event = 0;
+ }
+ break;
+ }
+
+ if (!v8event)
+ break;
+
+ if (elementName == "childrenEvents") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ int eventIndex = v8eventBuffer.key(v8event);
+ if (attributes.hasAttribute("list")) {
+ // store for later parsing (we haven't read all the events yet)
+ childrenIndexes[eventIndex] = attributes.value("list").toString();
+ }
+ if (attributes.hasAttribute("childrenTimes")) {
+ childrenTimes[eventIndex] =
+ attributes.value("childrenTimes").toString();
+ }
+ if (attributes.hasAttribute("parentTimes")) {
+ parentTimes[eventIndex] = attributes.value("parentTimes").toString();
+ }
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+ QString readData = stream.text().toString();
+
+ if (elementName == "displayname") {
+ v8event->displayName = readData;
+ break;
+ }
+
+ if (elementName == "functionname") {
+ v8event->functionName = readData;
+ break;
+ }
+
+ if (elementName == "filename") {
+ v8event->filename = readData;
+ break;
+ }
+
+ if (elementName == "line") {
+ v8event->line = readData.toInt();
+ break;
+ }
+
+ if (elementName == "totalTime") {
+ v8event->totalTime = readData.toDouble();
+ cumulatedV8Time += v8event->totalTime;
+ break;
+ }
+
+ if (elementName == "selfTime") {
+ v8event->selfTime = readData.toDouble();
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "v8profile") {
+ // done reading the v8 profile data
+ break;
+ }
+ }
+ default: break;
+ }
+}
+
+ // backwards compatibility
+ if (d->v8MeasuredTime == 0)
+ d->v8MeasuredTime = cumulatedV8Time;
+
+ // find v8events' children and parents
+ foreach (int parentIndex, childrenIndexes.keys()) {
+ QStringList childrenStrings = childrenIndexes.value(parentIndex).split(",");
+ QStringList childrenTimesStrings = childrenTimes.value(parentIndex).split(", ");
+ QStringList parentTimesStrings = parentTimes.value(parentIndex).split(", ");
+ for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
+ int childIndex = childrenStrings[ndx].toInt();
+ if (v8eventBuffer.value(childIndex)) {
+ QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]);
+ QV8EventSub *newParent = new QV8EventSub(v8eventBuffer[parentIndex]);
+ if (childrenTimesStrings.count() > ndx) {
+ newChild->totalTime = childrenTimesStrings[ndx].toDouble();
+ }
+ if (parentTimesStrings.count() > ndx) {
+ newParent->totalTime = parentTimesStrings[ndx].toDouble();
+ }
+ v8eventBuffer[parentIndex]->childrenHash.insert(
+ newChild->reference->displayName,
+ newChild);
+ v8eventBuffer[childIndex]->parentHash.insert(
+ newParent->reference->displayName,
+ newParent);
+ }
+ }
+ }
+
+ // store v8 events
+ foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) {
+ storedV8Event->eventHashStr =
+ QmlProfilerDataModel::getHashStringForV8Event(
+ storedV8Event->displayName, storedV8Event->functionName);
+ d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event;
+ }
+
+ d->collectV8Statistics();
+
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.h b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
new file mode 100644
index 0000000000..6441a24702
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
@@ -0,0 +1,79 @@
+#ifndef QV8PROFILERDATAMODEL_H
+#define QV8PROFILERDATAMODEL_H
+
+#include <QObject>
+#include <QHash>
+
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerDataModel;
+struct QV8EventSub;
+
+struct QV8EventData
+{
+ QV8EventData();
+ ~QV8EventData();
+
+ QString displayName;
+ QString eventHashStr;
+ QString filename;
+ QString functionName;
+ int line;
+ double totalTime; // given in milliseconds
+ double totalPercent;
+ double selfTime;
+ double selfPercent;
+ QHash <QString, QV8EventSub *> parentHash;
+ QHash <QString, QV8EventSub *> childrenHash;
+ int eventId;
+
+ QV8EventData &operator=(const QV8EventData &ref);
+};
+
+struct QV8EventSub {
+ QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {}
+ QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {}
+
+ QV8EventData *reference;
+ qint64 totalTime;
+};
+
+class QV8ProfilerDataModel : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QV8ProfilerDataModel(QObject *parent, QmlProfilerDataModel *profilerDataModel);
+ ~QV8ProfilerDataModel();
+
+ void clear();
+ bool isEmpty() const;
+ QList<QV8EventData *> getV8Events() const;
+ QV8EventData *v8EventDescription(int eventId) const;
+
+ qint64 v8MeasuredTime() const;
+ void collectV8Statistics();
+
+ void save(QXmlStreamWriter &stream);
+ void load(QXmlStreamReader &stream);
+
+public slots:
+ void addV8Event(int depth,
+ const QString &function,
+ const QString &filename,
+ int lineNumber,
+ double totalTime,
+ double selfTime);
+
+private:
+ class QV8ProfilerDataModelPrivate;
+ QV8ProfilerDataModelPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QV8PROFILERDATAMODEL_H
diff --git a/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.cpp b/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.cpp
index 7ca1b59b6c..7930cf3333 100644
--- a/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.cpp
+++ b/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.cpp
@@ -33,8 +33,8 @@
#include "remotelinuxqmlprofilerrunner.h"
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <remotelinux/portlist.h>
#include <remotelinux/remotelinuxapplicationrunner.h>
+#include <utils/portlist.h>
#include <utils/qtcassert.h>
using namespace ExtensionSystem;
@@ -45,7 +45,7 @@ using namespace RemoteLinux;
RemoteLinuxQmlProfilerRunner::RemoteLinuxQmlProfilerRunner(
RemoteLinuxRunConfiguration *runConfiguration, QObject *parent)
: AbstractQmlProfilerRunner(parent)
- , m_port(-1)
+ , m_port(0)
, m_runControl(0)
{
// find run control factory
@@ -96,7 +96,7 @@ void RemoteLinuxQmlProfilerRunner::stop()
runner()->stop();
}
-int RemoteLinuxQmlProfilerRunner::debugPort() const
+quint16 RemoteLinuxQmlProfilerRunner::debugPort() const
{
return m_port;
}
@@ -105,7 +105,7 @@ void RemoteLinuxQmlProfilerRunner::getPorts()
{
QTC_ASSERT(runner(), return);
m_port = runner()->freePorts()->getNext();
- if (m_port == -1) {
+ if (m_port == 0) {
emit appendMessage(tr("Not enough free ports on device for analyzing.\n"),
Utils::ErrorMessageFormat);
runner()->stop();
diff --git a/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.h b/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.h
index 7841a6ae7c..086e1aa33e 100644
--- a/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.h
+++ b/src/plugins/qmlprofiler/remotelinuxqmlprofilerrunner.h
@@ -53,7 +53,7 @@ public:
// AbstractQmlProfilerRunner
virtual void start();
virtual void stop();
- virtual int debugPort() const;
+ virtual quint16 debugPort() const;
private slots:
void getPorts();
@@ -67,7 +67,7 @@ private slots:
private:
RemoteLinux::AbstractRemoteLinuxApplicationRunner *runner() const;
- int m_port;
+ quint16 m_port;
RemoteLinux::AbstractRemoteLinuxRunControl *m_runControl;
};
diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelinerenderer.cpp
index 1e10eb0075..a078d31f84 100644
--- a/src/plugins/qmlprofiler/timelineview.cpp
+++ b/src/plugins/qmlprofiler/timelinerenderer.cpp
@@ -30,7 +30,7 @@
**
**************************************************************************/
-#include "timelineview.h"
+#include "timelinerenderer.h"
#include <qdeclarativecontext.h>
#include <qdeclarativeproperty.h>
@@ -45,9 +45,9 @@ using namespace QmlProfiler::Internal;
const int DefaultRowHeight = 30;
-TimelineView::TimelineView(QDeclarativeItem *parent) :
+TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) :
QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0),
- m_lastStartTime(0), m_lastEndTime(0), m_eventList(0)
+ m_lastStartTime(0), m_lastEndTime(0), m_profilerDataModel(0)
{
clearData();
setFlag(QGraphicsItem::ItemHasNoContents, false);
@@ -57,12 +57,12 @@ TimelineView::TimelineView(QDeclarativeItem *parent) :
m_rowsExpanded << false;
}
-void TimelineView::componentComplete()
+void TimelineRenderer::componentComplete()
{
const QMetaObject *metaObject = this->metaObject();
int propertyCount = metaObject->propertyCount();
int requestPaintMethod = metaObject->indexOfMethod("requestPaint()");
- for (int ii = TimelineView::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
+ for (int ii = TimelineRenderer::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
QMetaProperty p = metaObject->property(ii);
if (p.hasNotifySignal())
QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
@@ -70,12 +70,12 @@ void TimelineView::componentComplete()
QDeclarativeItem::componentComplete();
}
-void TimelineView::requestPaint()
+void TimelineRenderer::requestPaint()
{
update();
}
-void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
+void TimelineRenderer::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
{
qint64 windowDuration = m_endTime - m_startTime;
if (windowDuration <= 0)
@@ -86,7 +86,8 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
m_rowWidths.clear();
// The "1+" is because the reference screenshot features an empty row per type, in order to leave space for the title
for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) {
- m_rowWidths << 1 + (m_rowsExpanded[i] ? m_eventList->uniqueEventsOfType(i) : m_eventList->maxNestingForType(i));
+ m_rowWidths << 1 + (m_rowsExpanded[i] ? m_profilerDataModel->uniqueEventsOfType(i) :
+ m_profilerDataModel->maxNestingForType(i));
}
// event rows
@@ -105,8 +106,8 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
for (int j=0; j<m_rowWidths[i]; j++)
m_rowLastX << -m_startTime * m_spacing;
- int firstIndex = m_eventList->findFirstIndex(m_startTime);
- int lastIndex = m_eventList->findLastIndex(m_endTime);
+ int firstIndex = m_profilerDataModel->findFirstIndex(m_startTime);
+ int lastIndex = m_profilerDataModel->findLastIndex(m_endTime);
drawItemsToPainter(p, firstIndex, lastIndex);
drawSelectionBoxes(p, firstIndex, lastIndex);
@@ -116,25 +117,27 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
m_lastEndTime = m_endTime;
}
-QColor TimelineView::colorForItem(int itemIndex)
+QColor TimelineRenderer::colorForItem(int itemIndex)
{
- int ndx = m_eventList->getEventId(itemIndex);
+ int ndx = m_profilerDataModel->getEventId(itemIndex);
return QColor::fromHsl((ndx*25)%360, 76, 166);
}
-void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
{
int x, y, width, height, rowNumber, eventType;
for (int i = fromIndex; i <= toIndex; i++) {
- x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing;
+ x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(i) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
- width = m_eventList->getDuration(i)*m_spacing;
+ width = m_profilerDataModel->getDuration(i)*m_spacing;
if (width<1)
width = 1;
@@ -144,17 +147,19 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
m_rowLastX[rowNumber] = x+width;
// special: animations
- if (eventType == 0 && m_eventList->getAnimationCount(i) >= 0) {
- double scale = m_eventList->getMaximumAnimationCount() - m_eventList->getMinimumAnimationCount();
+ if (eventType == 0 && m_profilerDataModel->getAnimationCount(i) >= 0) {
+ double scale = m_profilerDataModel->getMaximumAnimationCount() -
+ m_profilerDataModel->getMinimumAnimationCount();
double fraction;
if (scale > 1)
- fraction = (double)(m_eventList->getAnimationCount(i) - m_eventList->getMinimumAnimationCount()) / scale;
+ fraction = (double)(m_profilerDataModel->getAnimationCount(i) -
+ m_profilerDataModel->getMinimumAnimationCount()) / scale;
else
fraction = 1.0;
height = DefaultRowHeight * (fraction * 0.85 + 0.15);
y += DefaultRowHeight - height;
- double fpsFraction = m_eventList->getFramerate(i) / 60.0;
+ double fpsFraction = m_profilerDataModel->getFramerate(i) / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166));
@@ -167,12 +172,12 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
}
}
-void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
{
if (m_selectedItem == -1)
return;
- int id = m_eventList->getEventId(m_selectedItem);
+ int id = m_profilerDataModel->getEventId(m_selectedItem);
p->setBrush(Qt::transparent);
QColor selectionColor = Qt::blue;
@@ -188,17 +193,19 @@ void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
QRect selectedItemRect(0,0,0,0);
for (int i = fromIndex; i <= toIndex; i++) {
- if (m_eventList->getEventId(i) != id)
+ if (m_profilerDataModel->getEventId(i) != id)
continue;
- x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(i) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
- width = m_eventList->getDuration(i)*m_spacing;
+ width = m_profilerDataModel->getDuration(i)*m_spacing;
if (width<1)
width = 1;
@@ -215,7 +222,7 @@ void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
}
}
-void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
{
int destindex;
int xfrom, xto, eventType;
@@ -228,35 +235,38 @@ void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toInde
p->save();
for (int i = fromIndex; i <= toIndex; i++) {
- destindex = m_eventList->getBindingLoopDest(i);
+ destindex = m_profilerDataModel->getBindingLoopDest(i);
if (destindex >= 0) {
// from
- xfrom = (m_eventList->getStartTime(i) + m_eventList->getDuration(i)/2 -
+ xfrom = (m_profilerDataModel->getStartTime(i) +
+ m_profilerDataModel->getDuration(i)/2 -
m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
yfrom = m_rowStarts[eventType] + DefaultRowHeight*
- (m_eventList->eventPosInType(i) + 1);
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- yfrom = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ yfrom = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
yfrom += DefaultRowHeight / 2;
// to
- xto = (m_eventList->getStartTime(destindex) + m_eventList->getDuration(destindex)/2 -
+ xto = (m_profilerDataModel->getStartTime(destindex) +
+ m_profilerDataModel->getDuration(destindex)/2 -
m_startTime) * m_spacing;
- eventType = m_eventList->getType(destindex);
+ eventType = m_profilerDataModel->getType(destindex);
if (m_rowsExpanded[eventType])
- yto = m_rowStarts[eventType] + DefaultRowHeight*
- (m_eventList->eventPosInType(destindex) + 1);
+ yto = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(destindex) + 1);
else
yto = m_rowStarts[eventType] + DefaultRowHeight *
- m_eventList->getNestingLevel(destindex);
+ m_profilerDataModel->getNestingLevel(destindex);
yto += DefaultRowHeight / 2;
// radius
- int eventWidth = m_eventList->getDuration(i) * m_spacing;
+ int eventWidth = m_profilerDataModel->getDuration(i) * m_spacing;
radius = 5;
if (radius * 2 > eventWidth)
radius = eventWidth / 2;
@@ -283,7 +293,7 @@ void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toInde
p->restore();
}
-void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// special case: if there is a drag area below me, don't accept the
// events unless I'm actually clicking inside an item
@@ -294,19 +304,19 @@ void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
-void TimelineView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
manageClicked();
}
-void TimelineView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(false);
}
-void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+void TimelineRenderer::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
manageHovered(event->pos().x(), event->pos().y());
@@ -314,7 +324,7 @@ void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
event->setAccepted(false);
}
-void TimelineView::manageClicked()
+void TimelineRenderer::manageClicked()
{
if (m_currentSelection.eventIndex != -1) {
if (m_currentSelection.eventIndex == m_selectedItem)
@@ -328,7 +338,7 @@ void TimelineView::manageClicked()
setSelectedItem(m_currentSelection.eventIndex);
}
-void TimelineView::manageHovered(int x, int y)
+void TimelineRenderer::manageHovered(int x, int y)
{
if (m_endTime - m_startTime <=0 || m_lastEndTime - m_lastStartTime <= 0)
return;
@@ -337,13 +347,16 @@ void TimelineView::manageHovered(int x, int y)
int row = y / DefaultRowHeight;
// already covered? nothing to do
- if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) {
+ if (m_currentSelection.eventIndex != -1 &&
+ time >= m_currentSelection.startTime &&
+ time <= m_currentSelection.endTime &&
+ row == m_currentSelection.row) {
return;
}
// find if there's items in the time range
- int eventFrom = m_eventList->findFirstIndex(time);
- int eventTo = m_eventList->findLastIndex(time);
+ int eventFrom = m_profilerDataModel->findFirstIndex(time);
+ int eventTo = m_profilerDataModel->findLastIndex(time);
if (eventTo < eventFrom) {
m_currentSelection.eventIndex = -1;
return;
@@ -352,19 +365,21 @@ void TimelineView::manageHovered(int x, int y)
// find if we are in the right column
int itemRow, eventType;
for (int i=eventTo; i>=eventFrom; --i) {
- if (ceil(m_eventList->getEndTime(i)*m_spacing) < floor(time*m_spacing))
+ if (ceil(m_profilerDataModel->getEndTime(i)*m_spacing) < floor(time*m_spacing))
continue;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->eventPosInType(i) + 1;
+ itemRow = m_rowStarts[eventType]/DefaultRowHeight +
+ m_profilerDataModel->eventPosInType(i) + 1;
else
- itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->getNestingLevel(i);
+ itemRow = m_rowStarts[eventType]/DefaultRowHeight +
+ m_profilerDataModel->getNestingLevel(i);
if (itemRow == row) {
// match
m_currentSelection.eventIndex = i;
- m_currentSelection.startTime = m_eventList->getStartTime(i);
- m_currentSelection.endTime = m_eventList->getEndTime(i);
+ m_currentSelection.startTime = m_profilerDataModel->getStartTime(i);
+ m_currentSelection.endTime = m_profilerDataModel->getEndTime(i);
m_currentSelection.row = row;
if (!m_selectionLocked)
setSelectedItem(i);
@@ -376,7 +391,7 @@ void TimelineView::manageHovered(int x, int y)
return;
}
-void TimelineView::clearData()
+void TimelineRenderer::clearData()
{
m_startTime = 0;
m_endTime = 0;
@@ -390,122 +405,125 @@ void TimelineView::clearData()
m_selectionLocked = true;
}
-qint64 TimelineView::getDuration(int index) const
+qint64 TimelineRenderer::getDuration(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getEndTime(index) - m_eventList->getStartTime(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getEndTime(index) -
+ m_profilerDataModel->getStartTime(index);
}
-QString TimelineView::getFilename(int index) const
+QString TimelineRenderer::getFilename(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getFilename(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getFilename(index);
}
-int TimelineView::getLine(int index) const
+int TimelineRenderer::getLine(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getLine(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getLine(index);
}
-QString TimelineView::getDetails(int index) const
+QString TimelineRenderer::getDetails(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getDetails(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getDetails(index);
}
-int TimelineView::getYPosition(int index) const
+int TimelineRenderer::getYPosition(int index) const
{
- Q_ASSERT(m_eventList);
- if (index >= m_eventList->count() || m_rowStarts.isEmpty())
+ Q_ASSERT(m_profilerDataModel);
+ if (index >= m_profilerDataModel->count() || m_rowStarts.isEmpty())
return 0;
- int y, eventType = m_eventList->getType(index);
+ int y, eventType = m_profilerDataModel->getType(index);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(index) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(index) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(index);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(index);
return y;
}
-void TimelineView::setRowExpanded(int rowIndex, bool expanded)
+void TimelineRenderer::setRowExpanded(int rowIndex, bool expanded)
{
m_rowsExpanded[rowIndex] = expanded;
update();
}
-void TimelineView::selectNext()
+void TimelineRenderer::selectNext()
{
- if (m_eventList->count() == 0)
+ if (m_profilerDataModel->count() == 0)
return;
// select next in view or after
int newIndex = m_selectedItem+1;
- if (newIndex >= m_eventList->count())
+ if (newIndex >= m_profilerDataModel->count())
newIndex = 0;
- if (m_eventList->getEndTime(newIndex) < m_startTime)
- newIndex = m_eventList->findFirstIndexNoParents(m_startTime);
+ if (m_profilerDataModel->getEndTime(newIndex) < m_startTime)
+ newIndex = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
setSelectedItem(newIndex);
}
-void TimelineView::selectPrev()
+void TimelineRenderer::selectPrev()
{
- if (m_eventList->count() == 0)
+ if (m_profilerDataModel->count() == 0)
return;
// select last in view or before
int newIndex = m_selectedItem-1;
if (newIndex < 0)
- newIndex = m_eventList->count()-1;
- if (m_eventList->getStartTime(newIndex) > m_endTime)
- newIndex = m_eventList->findLastIndex(m_endTime);
+ newIndex = m_profilerDataModel->count()-1;
+ if (m_profilerDataModel->getStartTime(newIndex) > m_endTime)
+ newIndex = m_profilerDataModel->findLastIndex(m_endTime);
setSelectedItem(newIndex);
}
-int TimelineView::nextItemFromId(int eventId) const
+int TimelineRenderer::nextItemFromId(int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_eventList->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
else
ndx = m_selectedItem + 1;
- if (ndx >= m_eventList->count())
+ if (ndx >= m_profilerDataModel->count())
ndx = 0;
int startIndex = ndx;
do {
- if (m_eventList->getEventId(ndx) == eventId)
+ if (m_profilerDataModel->getEventId(ndx) == eventId)
return ndx;
- ndx = (ndx + 1) % m_eventList->count();
+ ndx = (ndx + 1) % m_profilerDataModel->count();
} while (ndx != startIndex);
return -1;
}
-int TimelineView::prevItemFromId(int eventId) const
+int TimelineRenderer::prevItemFromId(int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_eventList->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
else
ndx = m_selectedItem - 1;
if (ndx < 0)
- ndx = m_eventList->count() - 1;
+ ndx = m_profilerDataModel->count() - 1;
int startIndex = ndx;
do {
- if (m_eventList->getEventId(ndx) == eventId)
+ if (m_profilerDataModel->getEventId(ndx) == eventId)
return ndx;
if (--ndx < 0)
- ndx = m_eventList->count()-1;
+ ndx = m_profilerDataModel->count()-1;
} while (ndx != startIndex);
return -1;
}
-void TimelineView::selectNextFromId(int eventId)
+void TimelineRenderer::selectNextFromId(int eventId)
{
int eventIndex = nextItemFromId(eventId);
if (eventIndex != -1)
setSelectedItem(eventIndex);
}
-void TimelineView::selectPrevFromId(int eventId)
+void TimelineRenderer::selectPrevFromId(int eventId)
{
int eventIndex = prevItemFromId(eventId);
if (eventIndex != -1)
diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelinerenderer.h
index 357662ea3e..6b776b4c04 100644
--- a/src/plugins/qmlprofiler/timelineview.h
+++ b/src/plugins/qmlprofiler/timelinerenderer.h
@@ -30,29 +30,29 @@
**
**************************************************************************/
-#ifndef TIMELINEVIEW_H
-#define TIMELINEVIEW_H
+#ifndef TIMELINERENDERER_H
+#define TIMELINERENDERER_H
#include <QDeclarativeItem>
#include <QScriptValue>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
+#include "qmlprofilerdatamodel.h"
namespace QmlProfiler {
namespace Internal {
-class TimelineView : public QDeclarativeItem
+class TimelineRenderer : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
- Q_PROPERTY(QObject* eventList READ eventList WRITE setEventList NOTIFY eventListChanged)
+ Q_PROPERTY(QObject* profilerDataModel READ profilerDataModel WRITE setProfilerDataModel NOTIFY profilerDataModelChanged)
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
public:
- explicit TimelineView(QDeclarativeItem *parent = 0);
+ explicit TimelineRenderer(QDeclarativeItem *parent = 0);
qint64 startTime() const
{
@@ -84,11 +84,11 @@ public:
return m_endDragArea;
}
- QmlJsDebugClient::QmlProfilerEventList *eventList() const { return m_eventList; }
- void setEventList(QObject *eventList)
+ QmlProfilerDataModel *profilerDataModel() const { return m_profilerDataModel; }
+ void setProfilerDataModel(QObject *profilerDataModel)
{
- m_eventList = qobject_cast<QmlJsDebugClient::QmlProfilerEventList *>(eventList);
- emit eventListChanged(m_eventList);
+ m_profilerDataModel = qobject_cast<QmlProfilerDataModel *>(profilerDataModel);
+ emit profilerDataModelChanged(m_profilerDataModel);
}
Q_INVOKABLE qint64 getDuration(int index) const;
@@ -109,7 +109,7 @@ public:
signals:
void startTimeChanged(qint64 arg);
void endTimeChanged(qint64 arg);
- void eventListChanged(QmlJsDebugClient::QmlProfilerEventList *list);
+ void profilerDataModelChanged(QmlProfilerDataModel *list);
void selectionLockedChanged(bool locked);
void selectedItemChanged(int itemIndex);
void startDragAreaChanged(int startDragArea);
@@ -195,7 +195,7 @@ private:
qint64 m_lastStartTime;
qint64 m_lastEndTime;
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
+ QmlProfilerDataModel *m_profilerDataModel;
QList<int> m_rowLastX;
QList<int> m_rowStarts;
@@ -218,6 +218,6 @@ private:
} // namespace Internal
} // namespace QmlProfiler
-QML_DECLARE_TYPE(QmlProfiler::Internal::TimelineView)
+QML_DECLARE_TYPE(QmlProfiler::Internal::TimelineRenderer)
-#endif // TIMELINEVIEW_H
+#endif // TIMELINERENDERER_H
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
deleted file mode 100644
index f075c49ac6..0000000000
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ /dev/null
@@ -1,666 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "tracewindow.h"
-
-#include "qmlprofilerplugin.h"
-
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include <qmljsdebugclient/qmlprofilertraceclient.h>
-#include <utils/styledbar.h>
-
-#include <QDeclarativeView>
-#include <QDeclarativeContext>
-#include <QVBoxLayout>
-#include <QGraphicsObject>
-#include <QContextMenuEvent>
-#include <QScrollBar>
-#include <QSlider>
-#include <QWidget>
-
-#include <math.h>
-
-using namespace QmlJsDebugClient;
-
-namespace QmlProfiler {
-namespace Internal {
-
-const int sliderTicks = 10000;
-const qreal sliderExp = 3;
-
-void ZoomControl::setRange(qint64 startTime, qint64 endTime)
-{
- if (m_startTime != startTime || m_endTime != endTime) {
- m_startTime = startTime;
- m_endTime = endTime;
- emit rangeChanged();
- }
-}
-
-ScrollableDeclarativeView::ScrollableDeclarativeView(QWidget *parent)
- : QDeclarativeView(parent)
-{
-}
-
-ScrollableDeclarativeView::~ScrollableDeclarativeView()
-{
-}
-
-void ScrollableDeclarativeView::scrollContentsBy(int dx, int dy)
-{
- // special workaround to track the scrollbar
- if (rootObject()) {
- int scrollY = rootObject()->property("scrollY").toInt();
- rootObject()->setProperty("scrollY", QVariant(scrollY - dy));
- }
- QDeclarativeView::scrollContentsBy(dx,dy);
-}
-
-TraceWindow::TraceWindow(QWidget *parent)
- : QWidget(parent)
-{
- setObjectName("QML Profiler");
-
- m_zoomControl = new ZoomControl(this);
- connect(m_zoomControl.data(), SIGNAL(rangeChanged()), this, SLOT(updateRange()));
-
- QVBoxLayout *groupLayout = new QVBoxLayout;
- groupLayout->setContentsMargins(0, 0, 0, 0);
- groupLayout->setSpacing(0);
-
- m_mainView = new ScrollableDeclarativeView(this);
- m_mainView->setResizeMode(QDeclarativeView::SizeViewToRootObject);
- m_mainView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- m_mainView->setBackgroundBrush(QBrush(Qt::white));
- m_mainView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
- m_mainView->setFocus();
-
- MouseWheelResizer *resizer = new MouseWheelResizer(this);
- connect(resizer,SIGNAL(mouseWheelMoved(int,int,int)), this, SLOT(mouseWheelMoved(int,int,int)));
- m_mainView->viewport()->installEventFilter(resizer);
-
- QHBoxLayout *toolsLayout = new QHBoxLayout;
-
- m_timebar = new QDeclarativeView(this);
- m_timebar->setResizeMode(QDeclarativeView::SizeRootObjectToView);
- m_timebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- m_timebar->setFixedHeight(24);
-
- m_overview = new QDeclarativeView(this);
- m_overview->setResizeMode(QDeclarativeView::SizeRootObjectToView);
- m_overview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- m_overview->setMaximumHeight(50);
-
- m_zoomToolbar = createZoomToolbar();
- m_zoomToolbar->move(0, m_timebar->height());
- m_zoomToolbar->setVisible(false);
-
- toolsLayout->addWidget(createToolbar());
- toolsLayout->addWidget(m_timebar);
-
- groupLayout->addLayout(toolsLayout);
- groupLayout->addWidget(m_mainView);
- groupLayout->addWidget(m_overview);
-
- setLayout(groupLayout);
-
- m_eventList = new QmlProfilerEventList(this);
- connect(this,SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)), m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- connect(this, SIGNAL(traceFinished(qint64)), m_eventList, SLOT(setTraceEndTime(qint64)));
- connect(this, SIGNAL(traceStarted(qint64)), m_eventList, SLOT(setTraceStartTime(qint64)));
- connect(this, SIGNAL(frameEvent(qint64,int,int)), m_eventList, SLOT(addFrameEvent(qint64,int,int)));
- connect(m_eventList, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- m_mainView->rootContext()->setContextProperty("qmlEventList", m_eventList);
- m_overview->rootContext()->setContextProperty("qmlEventList", m_eventList);
-
- m_rewriter = new QmlProfilerDetailsRewriter(this);
- connect(m_eventList, SIGNAL(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)), m_rewriter, SLOT(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)));
- connect(m_rewriter, SIGNAL(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)), m_eventList, SLOT(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)));
- connect(m_rewriter, SIGNAL(eventDetailsChanged()), m_eventList, SLOT(finishedRewritingDetails()));
- connect(m_eventList, SIGNAL(reloadDocumentsForDetails()), m_rewriter, SLOT(reloadDocuments()));
-
- connect(this, SIGNAL(v8range(int,QString,QString,int,double,double)), m_eventList, SLOT(addV8Event(int,QString,QString,int,double,double)));
-
- // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
- setMinimumHeight(170);
- m_currentZoomLevel = 0;
- m_profiledTime = 0;
-
- initializeQmlViews();
-}
-
-TraceWindow::~TraceWindow()
-{
- disconnectClientSignals();
- delete m_plugin.data();
- delete m_v8plugin.data();
-}
-
-QWidget *TraceWindow::createToolbar()
-{
- Utils::StyledBar *bar = new Utils::StyledBar(this);
- bar->setSingleRow(true);
- bar->setFixedWidth(150);
- bar->setFixedHeight(24);
-
- QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
- toolBarLayout->setMargin(0);
- toolBarLayout->setSpacing(0);
-
- QToolButton *buttonPrev= new QToolButton;
- buttonPrev->setIcon(QIcon(":/qmlprofiler/ico_prev.png"));
- buttonPrev->setToolTip(tr("Jump to previous event"));
- connect(buttonPrev, SIGNAL(clicked()), this, SIGNAL(jumpToPrev()));
- connect(this, SIGNAL(enableToolbar(bool)), buttonPrev, SLOT(setEnabled(bool)));
-
- QToolButton *buttonNext= new QToolButton;
- buttonNext->setIcon(QIcon(":/qmlprofiler/ico_next.png"));
- buttonNext->setToolTip(tr("Jump to next event"));
- connect(buttonNext, SIGNAL(clicked()), this, SIGNAL(jumpToNext()));
- connect(this, SIGNAL(enableToolbar(bool)), buttonNext, SLOT(setEnabled(bool)));
-
- QToolButton *buttonZoomControls = new QToolButton;
- buttonZoomControls->setIcon(QIcon(":/qmlprofiler/ico_zoom.png"));
- buttonZoomControls->setToolTip(tr("Show zoom slider"));
- buttonZoomControls->setCheckable(true);
- buttonZoomControls->setChecked(false);
- connect(buttonZoomControls, SIGNAL(toggled(bool)), m_zoomToolbar, SLOT(setVisible(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), buttonZoomControls, SLOT(setEnabled(bool)));
-
- m_buttonRange = new QToolButton;
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- m_buttonRange->setToolTip(tr("Select range"));
- m_buttonRange->setCheckable(true);
- m_buttonRange->setChecked(false);
- connect(m_buttonRange, SIGNAL(clicked(bool)), this, SLOT(toggleRangeMode(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), m_buttonRange, SLOT(setEnabled(bool)));
- connect(this, SIGNAL(rangeModeChanged(bool)), m_buttonRange, SLOT(setChecked(bool)));
-
- m_buttonLock = new QToolButton;
- m_buttonLock->setIcon(QIcon(":/qmlprofiler/ico_selectionmode.png"));
- m_buttonLock->setToolTip(tr("View event information on mouseover"));
- m_buttonLock->setCheckable(true);
- m_buttonLock->setChecked(false);
- connect(m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), m_buttonLock, SLOT(setEnabled(bool)));
- connect(this, SIGNAL(lockModeChanged(bool)), m_buttonLock, SLOT(setChecked(bool)));
-
- toolBarLayout->addWidget(buttonPrev);
- toolBarLayout->addWidget(buttonNext);
- toolBarLayout->addWidget(new Utils::StyledSeparator());
- toolBarLayout->addWidget(buttonZoomControls);
- toolBarLayout->addWidget(new Utils::StyledSeparator());
- toolBarLayout->addWidget(m_buttonRange);
- toolBarLayout->addWidget(m_buttonLock);
-
- return bar;
-}
-
-
-QWidget *TraceWindow::createZoomToolbar()
-{
- Utils::StyledBar *bar = new Utils::StyledBar(this);
- bar->setSingleRow(true);
- bar->setFixedWidth(150);
- bar->setFixedHeight(24);
-
- QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
- toolBarLayout->setMargin(0);
- toolBarLayout->setSpacing(0);
-
- QSlider *zoomSlider = new QSlider(Qt::Horizontal);
- zoomSlider->setFocusPolicy(Qt::NoFocus);
- zoomSlider->setRange(1, sliderTicks);
- zoomSlider->setInvertedAppearance(true);
- zoomSlider->setPageStep(sliderTicks/100);
- connect(this, SIGNAL(enableToolbar(bool)), zoomSlider, SLOT(setEnabled(bool)));
- connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
- connect(this, SIGNAL(zoomLevelChanged(int)), zoomSlider, SLOT(setValue(int)));
- zoomSlider->setStyleSheet("\
- QSlider:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #5a5a5a);\
- border: 1px #313131;\
- height: 20px;\
- margin: 0px 0px 0px 0px;\
- }\
- QSlider::add-page:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
- border: 1px #313131;\
- }\
- QSlider::sub-page:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
- border: 1px #313131;\
- }\
- ");
-
- toolBarLayout->addWidget(zoomSlider);
-
- return bar;
-}
-
-void TraceWindow::reset(QDeclarativeDebugConnection *conn)
-{
- disconnectClientSignals();
-
- delete m_plugin.data();
- m_plugin = new QmlProfilerTraceClient(conn);
- delete m_v8plugin.data();
- m_v8plugin = new QV8ProfilerClient(conn);
-
- connectClientSignals();
-
- m_v8DataReady = false;
- m_qmlDataReady = false;
-
- m_mainView->rootContext()->setContextProperty("connection", m_plugin.data());
-}
-
-void TraceWindow::initializeQmlViews()
-{
- m_mainView->rootContext()->setContextProperty("connection", m_plugin.data());
- m_mainView->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
- m_timebar->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
- m_overview->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
-
- m_timebar->setSource(QUrl("qrc:/qmlprofiler/TimeDisplay.qml"));
- m_overview->setSource(QUrl("qrc:/qmlprofiler/Overview.qml"));
-
- m_mainView->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
- m_mainView->rootObject()->setProperty("width", QVariant(width()));
- m_mainView->rootObject()->setProperty("candidateHeight", QVariant(height() - m_timebar->height() - m_overview->height()));
-
- updateToolbar();
-
- connect(m_mainView->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
- connect(m_mainView->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
- connect(m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
- connect(m_mainView->rootObject(), SIGNAL(updateLockButton()), this, SLOT(updateLockButton()));
- connect(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar()));
- connect(this, SIGNAL(jumpToPrev()), m_mainView->rootObject(), SLOT(prevEvent()));
- connect(this, SIGNAL(jumpToNext()), m_mainView->rootObject(), SLOT(nextEvent()));
- connect(this, SIGNAL(updateViewZoom(QVariant)), m_mainView->rootObject(), SLOT(updateWindowLength(QVariant)));
- connect(this, SIGNAL(wheelZoom(QVariant,QVariant)), m_mainView->rootObject(), SLOT(wheelZoom(QVariant,QVariant)));
- connect(this, SIGNAL(globalZoom()), m_mainView->rootObject(), SLOT(globalZoom()));
- connect(this, SIGNAL(selectNextEventInDisplay(QVariant)), m_mainView->rootObject(), SLOT(selectNextWithId(QVariant)));
- connect(m_mainView->rootObject(), SIGNAL(selectedEventIdChanged(int)), this, SIGNAL(selectedEventIdChanged(int)));
- connect(m_mainView->rootObject(), SIGNAL(changeToolTip(QString)), this, SLOT(updateToolTip(QString)));
- connect(m_mainView->rootObject(), SIGNAL(updateVerticalScroll(int)), this, SLOT(updateVerticalScroll(int)));
-
- connect(this, SIGNAL(internalClearDisplay()), m_mainView->rootObject(), SLOT(clearAll()));
- connect(this,SIGNAL(internalClearDisplay()), m_overview->rootObject(), SLOT(clearDisplay()));
-}
-
-void TraceWindow::connectClientSignals()
-{
- if (m_plugin) {
- connect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
- connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
- this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- connect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
- connect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
- connect(m_plugin.data(), SIGNAL(frame(qint64,int,int)), this, SIGNAL(frameEvent(qint64,int,int)));
- connect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- connect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
- connect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
- }
- if (m_v8plugin) {
- connect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
- connect(m_v8plugin.data(), SIGNAL(v8range(int,QString,QString,int,double,double)), this, SIGNAL(v8range(int,QString,QString,int,double,double)));
- connect(m_v8plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- connect(m_v8plugin.data(), SIGNAL(enabledChanged()), m_v8plugin.data(), SLOT(sendRecordingStatus()));
- }
-}
-
-void TraceWindow::disconnectClientSignals()
-{
- if (m_plugin) {
- disconnect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
- disconnect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
- this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- disconnect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
- disconnect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
- disconnect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- disconnect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
- disconnect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
- }
- if (m_v8plugin) {
- disconnect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
- disconnect(m_v8plugin.data(), SIGNAL(v8range(int,QString,QString,int,double,double)), this, SIGNAL(v8range(int,QString,QString,int,double,double)));
- disconnect(m_v8plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- disconnect(m_v8plugin.data(), SIGNAL(enabledChanged()), m_v8plugin.data(), SLOT(sendRecordingStatus()));
- }
-}
-
-QmlProfilerEventList *TraceWindow::getEventList() const
-{
- return m_eventList;
-}
-
-ZoomControl *TraceWindow::rangeTimes() const
-{
- return m_zoomControl.data();
-}
-
-void TraceWindow::contextMenuEvent(QContextMenuEvent *ev)
-{
- emit contextMenuRequested(ev->globalPos());
-}
-
-void TraceWindow::updateCursorPosition()
-{
- emit gotoSourceLocation(m_mainView->rootObject()->property("fileName").toString(),
- m_mainView->rootObject()->property("lineNumber").toInt(),
- m_mainView->rootObject()->property("columnNumber").toInt());
-}
-
-void TraceWindow::updateTimer()
-{
- m_profiledTime = m_mainView->rootObject()->property("elapsedTime").toDouble();
-}
-
-void TraceWindow::correctTimer()
-{
- // once the data is post-processed, use the eventlist time instead of the qml timer
- m_profiledTime = (m_eventList->traceEndTime() - m_eventList->traceStartTime()) / 1.0e9;
- if (m_profiledTime < 0)
- m_profiledTime = 0;
- emit viewUpdated();
-}
-
-double TraceWindow::profiledTime() const
-{
- return m_profiledTime;
-}
-
-void TraceWindow::clearDisplay()
-{
- m_eventList->clear();
-
- if (m_plugin)
- m_plugin.data()->clearData();
- if (m_v8plugin)
- m_v8plugin.data()->clearData();
-
- m_zoomControl.data()->setRange(0,0);
- m_profiledTime = 0;
-
- updateVerticalScroll(0);
- m_mainView->rootObject()->setProperty("scrollY", QVariant(0));
-
- emit internalClearDisplay();
-}
-
-void TraceWindow::updateToolbar()
-{
- emit enableToolbar(m_eventList && m_eventList->count()>0);
-}
-
-void TraceWindow::toggleRangeMode(bool active)
-{
- bool rangeMode = m_mainView->rootObject()->property("selectionRangeMode").toBool();
- if (active != rangeMode) {
- if (active)
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
- else
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- m_mainView->rootObject()->setProperty("selectionRangeMode", QVariant(active));
- }
-}
-
-void TraceWindow::updateRangeButton()
-{
- bool rangeMode = m_mainView->rootObject()->property("selectionRangeMode").toBool();
- if (rangeMode)
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
- else
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- emit rangeModeChanged(rangeMode);
-}
-
-void TraceWindow::toggleLockMode(bool active)
-{
- bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool();
- if (active != lockMode) {
- m_mainView->rootObject()->setProperty("selectionLocked", QVariant(!active));
- m_mainView->rootObject()->setProperty("selectedItem", QVariant(-1));
- }
-}
-
-void TraceWindow::updateLockButton()
-{
- bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool();
- emit lockModeChanged(lockMode);
-}
-
-void TraceWindow::setRecording(bool recording)
-{
- if (recording) {
- m_v8DataReady = false;
- m_qmlDataReady = false;
- }
- if (m_plugin)
- m_plugin.data()->setRecording(recording);
- if (m_v8plugin)
- m_v8plugin.data()->setRecording(recording);
-}
-
-bool TraceWindow::isRecording() const
-{
- return m_plugin.data()->isRecording();
-}
-
-void TraceWindow::qmlComplete()
-{
- m_qmlDataReady = true;
- if (!m_v8plugin || m_v8plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_v8DataReady) {
- m_eventList->complete();
-
- // if no data was received, still notify completion
- if (m_eventList->currentState() == QmlProfilerEventList::Empty)
- emit viewUpdated();
-
- // once complete is sent, reset the flags
- m_qmlDataReady = false;
- m_v8DataReady = false;
- }
-}
-
-void TraceWindow::v8Complete()
-{
- m_v8DataReady = true;
- if (!m_plugin || m_plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_qmlDataReady) {
- m_eventList->complete();
-
- // if no data was received, still notify completion
- if (m_eventList->currentState() == QmlProfilerEventList::Empty)
- emit viewUpdated();
-
- // once complete is sent, reset the flags
- m_v8DataReady = false;
- m_qmlDataReady = false;
- }
-}
-
-void TraceWindow::resizeEvent(QResizeEvent *event)
-{
- if (m_mainView->rootObject()) {
- m_mainView->rootObject()->setProperty("width", QVariant(event->size().width()));
- int newHeight = event->size().height() - m_timebar->height() - m_overview->height();
- m_mainView->rootObject()->setProperty("candidateHeight", QVariant(newHeight));
- }
-}
-
-bool MouseWheelResizer::eventFilter(QObject *obj, QEvent *event)
-{
- if (event->type() == QEvent::Wheel) {
- QWheelEvent *ev = static_cast<QWheelEvent *>(event);
- if (ev->modifiers() & Qt::ControlModifier) {
- emit mouseWheelMoved(ev->pos().x(), ev->pos().y(), ev->delta());
- return true;
- }
- }
- return QObject::eventFilter(obj, event);
-}
-
-void TraceWindow::mouseWheelMoved(int x, int y, int delta)
-{
- Q_UNUSED(y);
- if (m_mainView->rootObject()) {
- emit wheelZoom(QVariant(x), QVariant(delta));
- }
-}
-
-void TraceWindow::viewAll()
-{
- emit globalZoom();
-}
-
-bool TraceWindow::hasValidSelection() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeReady").toBool();
- }
- return false;
-}
-
-qint64 TraceWindow::selectionStart() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeStart").toLongLong();
- }
- return 0;
-}
-
-qint64 TraceWindow::selectionEnd() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeEnd").toLongLong();
- }
- return 0;
-}
-
-void TraceWindow::setZoomLevel(int zoomLevel)
-{
- if (m_currentZoomLevel != zoomLevel && m_mainView->rootObject()) {
- qreal newFactor = pow(qreal(zoomLevel) / qreal(sliderTicks), sliderExp);
- m_currentZoomLevel = zoomLevel;
- emit updateViewZoom(QVariant(newFactor));
- }
-}
-
-void TraceWindow::updateRange()
-{
- if (!m_eventList)
- return;
- qreal duration = m_zoomControl.data()->endTime() - m_zoomControl.data()->startTime();
- if (duration <= 0)
- return;
- if (m_eventList->traceDuration() <= 0)
- return;
- int newLevel = pow(duration / m_eventList->traceDuration(), 1/sliderExp) * sliderTicks;
- if (m_currentZoomLevel != newLevel) {
- m_currentZoomLevel = newLevel;
- emit zoomLevelChanged(newLevel);
- }
-}
-
-void TraceWindow::selectNextEvent(int eventId)
-{
- emit selectNextEventInDisplay(QVariant(eventId));
-}
-
-void TraceWindow::updateProfilerState()
-{
- bool qmlActive = false;
- bool v8Active = false;
- if (m_plugin)
- qmlActive = m_plugin.data()->isEnabled();
- if (m_v8plugin)
- v8Active = m_v8plugin.data()->isEnabled();
-
- emit profilerStateChanged(qmlActive, v8Active);
-}
-
-void TraceWindow::updateToolTip(const QString &text)
-{
- setToolTip(text);
-}
-
-void TraceWindow::updateVerticalScroll(int newPosition)
-{
- m_mainView->verticalScrollBar()->setValue(newPosition);
-}
-
-void TraceWindow::eventListStateChanged()
-{
- switch (m_eventList->currentState()) {
- case QmlProfilerEventList::Empty :
- clearDisplay();
- break;
- case QmlProfilerEventList::AcquiringData :
- firstDataReceived();
- break;
- case QmlProfilerEventList::ProcessingData :
- // nothing to be done
- break;
- case QmlProfilerEventList::Done :
- correctTimer();
- break;
- default:
- break;
- }
-}
-
-void TraceWindow::manageTraceStart(qint64 traceStart)
-{
- // new trace started
- emit clearViewsFromTool();
-
- emit traceStarted(traceStart);
-}
-
-void TraceWindow::firstDataReceived()
-{
- if (m_plugin && m_plugin.data()->isRecording()) {
- // serverside recording disabled
- m_plugin.data()->setRecordingFromServer(false);
- }
-}
-
-void TraceWindow::applicationDied()
-{
- if (m_mainView->rootObject())
- m_mainView->rootObject()->setProperty("applicationDied",QVariant(true));
-}
-
-} // namespace Internal
-} // namespace QmlProfiler
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index e3f454df8a..c05d6b78b8 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -272,11 +272,6 @@ ProjectExplorer::IProjectManager *QmlProject::projectManager() const
return m_manager;
}
-QList<ProjectExplorer::Project *> QmlProject::dependsOn()
-{
- return QList<Project *>();
-}
-
QList<ProjectExplorer::BuildConfigWidget*> QmlProject::subConfigWidgets()
{
return QList<ProjectExplorer::BuildConfigWidget*>();
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index 1d70f3025f..2c35a7b835 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -74,8 +74,6 @@ public:
ProjectExplorer::IProjectManager *projectManager() const;
Internal::QmlProjectTarget *activeTarget() const;
- QList<ProjectExplorer::Project *> dependsOn();
-
QList<ProjectExplorer::BuildConfigWidget*> subConfigWidgets();
ProjectExplorer::ProjectNode *rootProjectNode() const;
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
index 4885be0441..cd40ae3e2f 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
@@ -1,7 +1,12 @@
TEMPLATE = lib
TARGET = QmlProjectManager
-QT += declarative network
+QT += network
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
include(../../qtcreatorplugin.pri)
include(qmlprojectmanager_dependencies.pri)
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.h b/src/plugins/qmlprojectmanager/qmlprojectplugin.h
index 224c572aac..c9b10b7fe7 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.h
@@ -42,6 +42,7 @@ namespace QmlProjectManager {
class QMLPROJECTMANAGER_EXPORT QmlProjectPlugin: public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlProjectManager.json")
public:
QmlProjectPlugin();
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 3899c87435..8f18d89e9c 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -364,7 +364,7 @@ void QmlProjectRunConfiguration::updateEnabled()
// Always emit change signal to force reevaluation of run/debug buttons
m_isEnabled = newValue;
- emit isEnabledChanged(m_isEnabled);
+ emit enabledChanged();
}
void QmlProjectRunConfiguration::updateQtVersions()
diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp
index 25dfee554b..0041fb9ebc 100644
--- a/src/plugins/qt4projectmanager/profileeditor.cpp
+++ b/src/plugins/qt4projectmanager/profileeditor.cpp
@@ -233,11 +233,6 @@ void ProFileEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
highlighter->rehighlight();
}
-void ProFileEditorWidget::jumpToFile()
-{
- openLink(findLinkAt(textCursor()));
-}
-
//
// ProFileDocument
//
diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h
index e2d153041d..1d143eb7d2 100644
--- a/src/plugins/qt4projectmanager/profileeditor.h
+++ b/src/plugins/qt4projectmanager/profileeditor.h
@@ -88,7 +88,6 @@ protected:
public slots:
virtual void setFontSettings(const TextEditor::FontSettings &);
- void jumpToFile();
private:
ProFileEditorFactory *m_factory;
diff --git a/src/plugins/qt4projectmanager/qt-desktop/desktopqtversion.cpp b/src/plugins/qt4projectmanager/qt-desktop/desktopqtversion.cpp
index 0fc6e30a5b..c0146d2896 100644
--- a/src/plugins/qt4projectmanager/qt-desktop/desktopqtversion.cpp
+++ b/src/plugins/qt4projectmanager/qt-desktop/desktopqtversion.cpp
@@ -81,7 +81,6 @@ QString DesktopQtVersion::warningReason() const
QList<ProjectExplorer::Abi> DesktopQtVersion::detectQtAbis() const
{
- ensureMkSpecParsed();
return qtAbisFromLibrary(qtCorePath(versionInfo(), qtVersionString()));
}
diff --git a/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.cpp b/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.cpp
index 185fd3d462..cc61c89869 100644
--- a/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.cpp
@@ -165,7 +165,7 @@ void Qt4RunConfiguration::proFileUpdated(Qt4ProjectManager::Qt4ProFileNode *pro,
m_parseSuccess = success;
m_parseInProgress = parseInProgress;
if (enabled != isEnabled())
- emit isEnabledChanged(!enabled);
+ emit enabledChanged();
if (!parseInProgress) {
emit effectiveTargetInformationChanged();
@@ -287,7 +287,7 @@ Qt4RunConfigurationWidget::Qt4RunConfigurationWidget(Qt4RunConfiguration *qt4Run
m_environmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
vboxTopLayout->addWidget(m_environmentWidget);
- runConfigurationEnabledChange(m_qt4RunConfiguration->isEnabled());
+ runConfigurationEnabledChange();
connect(m_workingDirectoryEdit, SIGNAL(changed(QString)),
this, SLOT(workDirectoryEdited()));
@@ -321,8 +321,8 @@ Qt4RunConfigurationWidget::Qt4RunConfigurationWidget(Qt4RunConfiguration *qt4Run
connect(qt4RunConfiguration, SIGNAL(baseEnvironmentChanged()),
this, SLOT(baseEnvironmentChanged()));
- connect(qt4RunConfiguration, SIGNAL(isEnabledChanged(bool)),
- this, SLOT(runConfigurationEnabledChange(bool)));
+ connect(qt4RunConfiguration, SIGNAL(enabledChanged()),
+ this, SLOT(runConfigurationEnabledChange()));
}
Qt4RunConfigurationWidget::~Qt4RunConfigurationWidget()
@@ -363,8 +363,9 @@ void Qt4RunConfigurationWidget::userChangesEdited()
m_ignoreChange = false;
}
-void Qt4RunConfigurationWidget::runConfigurationEnabledChange(bool enabled)
+void Qt4RunConfigurationWidget::runConfigurationEnabledChange()
{
+ bool enabled = m_qt4RunConfiguration->isEnabled();
m_detailsContainer->setEnabled(enabled);
m_environmentWidget->setEnabled(enabled);
m_disabledIcon->setVisible(!enabled);
diff --git a/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.h b/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.h
index 4762b8edec..062b013d4f 100644
--- a/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-desktop/qt4runconfiguration.h
@@ -169,7 +169,7 @@ protected:
void hideEvent(QHideEvent *event);
private slots:
- void runConfigurationEnabledChange(bool);
+ void runConfigurationEnabledChange();
void workDirectoryEdited();
void workingDirectoryReseted();
void argumentsEdited(const QString &arguments);
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.cpp
index f7d5d95bb6..fd892ef067 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.cpp
@@ -193,7 +193,7 @@ void S60DeviceDebugRunControl::codaConnected()
engine()->handleRemoteSetupDone(-1, -1); // calls notifyInferiorSetupOk()
}
-void S60DeviceDebugRunControl::qmlEngineStateChanged(const Debugger::DebuggerState &state)
+void S60DeviceDebugRunControl::qmlEngineStateChanged(Debugger::DebuggerState state)
{
if (state == Debugger::EngineRunRequested)
m_codaRunControl->run();
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.h b/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.h
index 4ad373391a..c86ac0823c 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicedebugruncontrol.h
@@ -56,7 +56,7 @@ public:
private slots:
void remoteSetupRequested();
void codaConnected();
- void qmlEngineStateChanged(const Debugger::DebuggerState &state);
+ void qmlEngineStateChanged(Debugger::DebuggerState state);
void codaFinished();
void handleDebuggingFinished();
void handleMessageFromCoda(ProjectExplorer::RunControl *aCodaRunControl, const QString &msg, Utils::OutputFormat format);
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
index 54bf0c2164..ce74f2e8fa 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
@@ -113,7 +113,7 @@ void S60DeviceRunConfiguration::proFileUpdate(Qt4ProjectManager::Qt4ProFileNode
m_validParse = success;
m_parseInProgress = parseInProgress;
if (enabled != isEnabled())
- emit isEnabledChanged(!enabled);
+ emit enabledChanged();
if (!parseInProgress)
emit targetInformationChanged();
}
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
index f4034b906c..bba25dbf56 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
@@ -86,10 +86,10 @@ S60DeviceRunConfigurationWidget::S60DeviceRunConfigurationWidget(
connect(m_argumentsLineEdit, SIGNAL(textEdited(QString)),
this, SLOT(argumentsEdited(QString)));
- connect(m_runConfiguration, SIGNAL(isEnabledChanged(bool)),
- this, SLOT(runConfigurationEnabledChange(bool)));
+ connect(m_runConfiguration, SIGNAL(enabledChanged()),
+ this, SLOT(runConfigurationEnabledChange()));
- runConfigurationEnabledChange(m_runConfiguration->isEnabled());
+ runConfigurationEnabledChange();
}
void S60DeviceRunConfigurationWidget::argumentsEdited(const QString &text)
@@ -97,8 +97,9 @@ void S60DeviceRunConfigurationWidget::argumentsEdited(const QString &text)
m_runConfiguration->setCommandLineArguments(text.trimmed());
}
-void S60DeviceRunConfigurationWidget::runConfigurationEnabledChange(bool enabled)
+void S60DeviceRunConfigurationWidget::runConfigurationEnabledChange()
{
+ bool enabled = m_runConfiguration->isEnabled();
m_detailsWidget->setEnabled(enabled);
m_disabledIcon->setVisible(!enabled);
m_disabledReason->setVisible(!enabled);
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
index f5f69048a4..7a1ab41879 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
@@ -56,7 +56,7 @@ public:
QWidget *parent = 0);
private slots:
void argumentsEdited(const QString &text);
- void runConfigurationEnabledChange(bool enabled);
+ void runConfigurationEnabledChange();
private:
S60DeviceRunConfiguration *m_runConfiguration;
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
index 1a186a0d07..1a1a2146a7 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
@@ -116,7 +116,7 @@ void S60EmulatorRunConfiguration::proFileUpdate(Qt4ProjectManager::Qt4ProFileNod
m_validParse = success;
m_parseInProgress = parseInProgress;
if (enabled != isEnabled()) {
- emit isEnabledChanged(!enabled);
+ emit enabledChanged();
}
if (parseInProgress)
emit targetInformationChanged();
@@ -244,10 +244,10 @@ S60EmulatorRunConfigurationWidget::S60EmulatorRunConfigurationWidget(S60Emulator
connect(m_runConfiguration, SIGNAL(targetInformationChanged()),
this, SLOT(updateTargetInformation()));
- connect(m_runConfiguration, SIGNAL(isEnabledChanged(bool)),
- this, SLOT(runConfigurationEnabledChange(bool)));
+ connect(m_runConfiguration, SIGNAL(enabledChanged()),
+ this, SLOT(runConfigurationEnabledChange()));
- runConfigurationEnabledChange(m_runConfiguration->isEnabled());
+ runConfigurationEnabledChange();
}
void S60EmulatorRunConfigurationWidget::updateTargetInformation()
@@ -255,8 +255,9 @@ void S60EmulatorRunConfigurationWidget::updateTargetInformation()
m_executableLabel->setText(m_runConfiguration->executable());
}
-void S60EmulatorRunConfigurationWidget::runConfigurationEnabledChange(bool enabled)
+void S60EmulatorRunConfigurationWidget::runConfigurationEnabledChange()
{
+ bool enabled = m_runConfiguration->isEnabled();
m_detailsWidget->setEnabled(enabled);
m_disabledIcon->setVisible(!enabled);
m_disabledReason->setVisible(!enabled);
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
index 1ce10f83fd..be38f4c238 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
@@ -108,7 +108,7 @@ public:
private slots:
void updateTargetInformation();
- void runConfigurationEnabledChange(bool enabled);
+ void runConfigurationEnabledChange();
private:
S60EmulatorRunConfiguration *m_runConfiguration;
diff --git a/src/plugins/qt4projectmanager/qt-s60/sbsv2parser.cpp b/src/plugins/qt4projectmanager/qt-s60/sbsv2parser.cpp
index 97754fe163..1f5f980135 100644
--- a/src/plugins/qt4projectmanager/qt-s60/sbsv2parser.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/sbsv2parser.cpp
@@ -32,7 +32,7 @@
#include "sbsv2parser.h"
-#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
@@ -55,8 +55,7 @@ SbsV2Parser::SbsV2Parser() :
m_hub(0)
{
setObjectName(QLatin1String("SbsV2Parser"));
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
- m_hub = pm->getObject<TaskHub>();
+ m_hub = ProjectExplorerPlugin::instance()->taskHub();
}
void SbsV2Parser::stdOutput(const QString &line)
diff --git a/src/plugins/qt4projectmanager/qt4buildconfiguration.cpp b/src/plugins/qt4projectmanager/qt4buildconfiguration.cpp
index 7e73d48501..35cecaee6a 100644
--- a/src/plugins/qt4projectmanager/qt4buildconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt4buildconfiguration.cpp
@@ -673,14 +673,6 @@ Qt4BuildConfigurationFactory::~Qt4BuildConfigurationFactory()
void Qt4BuildConfigurationFactory::update()
{
- m_versions.clear();
- QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
- foreach (QtSupport::BaseQtVersion *version, vm->validVersions()) {
- QString key = QString::fromLatin1(QT4_BC_ID_PREFIX)
- + QString::fromLatin1("Qt%1").arg(version->uniqueId());
- VersionInfo info(tr("Using Qt Version \"%1\"").arg(version->displayName()), version->uniqueId());
- m_versions.insert(key, info);
- }
emit availableCreationIdsChanged();
}
@@ -691,30 +683,46 @@ QStringList Qt4BuildConfigurationFactory::availableCreationIds(ProjectExplorer::
QStringList results;
QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
- for (QMap<QString, VersionInfo>::const_iterator i = m_versions.constBegin();
- i != m_versions.constEnd(); ++i) {
- if (vm->version(i.value().versionId)->supportsTargetId(parent->id())
- && vm->version(i.value().versionId)->toolChainAvailable(parent->id()))
- results.append(i.key());
+ QList<QtSupport::BaseQtVersion *> versions = vm->versionsForTargetId(parent->id());
+ foreach (QtSupport::BaseQtVersion *v, versions) {
+ if (v->toolChainAvailable(parent->id()))
+ results << QLatin1String(QT4_BC_ID_PREFIX) + QString::number(v->uniqueId());
}
+
+
return results;
}
+int idToUniqueId(const QString &id)
+{
+ QString rest = id.mid(QString(QT4_BC_ID_PREFIX).length());
+ bool ok;
+ int unqieuid = rest.toInt(&ok);
+ if (!ok)
+ return -1;
+ return unqieuid;
+}
+
QString Qt4BuildConfigurationFactory::displayNameForId(const QString &id) const
{
- if (!m_versions.contains(id))
+ if (!id.startsWith(QT4_BC_ID_PREFIX))
+ return QString();
+
+ QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
+ QtSupport::BaseQtVersion *v = vm->version(idToUniqueId(id));
+ if (!v)
return QString();
- return m_versions.value(id).displayName;
+ return tr("Using Qt Version \"%1\"").arg(v->displayName());
}
bool Qt4BuildConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const QString &id) const
{
if (!qobject_cast<Qt4BaseTarget *>(parent))
return false;
- if (!m_versions.contains(id))
+ if (!id.startsWith(QT4_BC_ID_PREFIX))
return false;
- const VersionInfo &info = m_versions.value(id);
- QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(info.versionId);
+
+ QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(idToUniqueId(id));
if (!version ||
!version->supportsTargetId(parent->id()))
return false;
@@ -726,8 +734,7 @@ BuildConfiguration *Qt4BuildConfigurationFactory::create(ProjectExplorer::Target
if (!canCreate(parent, id))
return 0;
- const VersionInfo &info = m_versions.value(id);
- QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(info.versionId);
+ QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(idToUniqueId(id));
Q_ASSERT(version);
Qt4BaseTarget *qt4Target = static_cast<Qt4BaseTarget *>(parent);
diff --git a/src/plugins/qt4projectmanager/qt4buildconfiguration.h b/src/plugins/qt4projectmanager/qt4buildconfiguration.h
index 17b98abd0e..8fb8a9deb7 100644
--- a/src/plugins/qt4projectmanager/qt4buildconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt4buildconfiguration.h
@@ -188,20 +188,6 @@ public:
private slots:
void update();
-
-private:
- struct VersionInfo {
- VersionInfo()
- : versionId(-1)
- {}
- VersionInfo(const QString &d, int v)
- : displayName(d), versionId(v)
- {}
- QString displayName;
- int versionId;
- };
-
- QMap<QString, VersionInfo> m_versions;
};
} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp
index 002eb5fd1a..54c4a91e7a 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.cpp
+++ b/src/plugins/qt4projectmanager/qt4nodes.cpp
@@ -50,7 +50,7 @@
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/projectexplorer.h>
@@ -107,8 +107,10 @@ static const FileTypeDataStorage fileTypeDataStorage[] = {
":/qt4projectmanager/images/unknown.png" }
};
-struct Qt4NodeStaticData {
- struct FileTypeData {
+class Qt4NodeStaticData {
+public:
+ class FileTypeData {
+ public:
FileTypeData(ProjectExplorer::FileType t = ProjectExplorer::UnknownFileType,
const QString &tN = QString(),
const QIcon &i = QIcon()) :
@@ -119,16 +121,19 @@ struct Qt4NodeStaticData {
QIcon icon;
};
+ Qt4NodeStaticData();
+
QVector<FileTypeData> fileTypeData;
QIcon projectIcon;
};
static void clearQt4NodeStaticData();
-Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {
+Qt4NodeStaticData::Qt4NodeStaticData()
+{
// File type data
const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
- x->fileTypeData.reserve(count);
+ fileTypeData.reserve(count);
// Overlay the SP_DirIcon with the custom icons
const QSize desiredSize = QSize(16, 16);
@@ -141,18 +146,20 @@ Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {
QIcon folderIcon;
folderIcon.addPixmap(folderPixmap);
const QString desc = Qt4ProjectManager::Qt4PriFileNode::tr(fileTypeDataStorage[i].typeName);
- x->fileTypeData.push_back(Qt4NodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
- desc, folderIcon));
+ fileTypeData.push_back(Qt4NodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
+ desc, folderIcon));
}
// Project icon
const QIcon projectBaseIcon(QLatin1String(":/qt4projectmanager/images/qt_project.png"));
const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
projectBaseIcon,
desiredSize);
- x->projectIcon.addPixmap(projectPixmap);
+ projectIcon.addPixmap(projectPixmap);
qAddPostRoutine(clearQt4NodeStaticData);
-})
+}
+
+Q_GLOBAL_STATIC(Qt4NodeStaticData, qt4NodeStaticData)
static void clearQt4NodeStaticData()
{
diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp
index e935e5cea3..59f45cda4e 100644
--- a/src/plugins/qt4projectmanager/qt4project.cpp
+++ b/src/plugins/qt4projectmanager/qt4project.cpp
@@ -41,6 +41,8 @@
#include "qt4projectmanagerconstants.h"
#include "qt4buildconfiguration.h"
#include "findqt4profiles.h"
+#include "qt4basetargetfactory.h"
+#include "buildconfigurationinfo.h"
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
@@ -49,7 +51,7 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/headerpath.h>
@@ -540,7 +542,7 @@ void Qt4Project::updateCppCodeModel()
part->language = CPlusPlus::CppModelManagerInterface::CXX;
// part->flags
if (tc)
- part->flags = tc->compilerFlags(pro->variableValue(CppFlagsVar));
+ part->cxx11Enabled = tc->compilerFlags(pro->variableValue(CppFlagsVar)) == ToolChain::STD_CXX11;
part->sourceFiles = pro->variableValue(CppSourceVar);
pinfo.appendProjectPart(part);
@@ -909,12 +911,6 @@ QString Qt4Project::generatedUiHeader(const QString &formFile) const
return QString();
}
-QList<ProjectExplorer::Project*> Qt4Project::dependsOn()
-{
- // NBS implement dependsOn
- return QList<Project *>();
-}
-
void Qt4Project::proFileParseError(const QString &errorMessage)
{
Core::ICore::messageManager()->printToOutputPanePopup(errorMessage);
@@ -1334,6 +1330,36 @@ bool Qt4Project::needsConfiguration() const
return targets().isEmpty();
}
+void Qt4Project::configureAsExampleProject(const QStringList &platforms)
+{
+ QList<Qt4BaseTargetFactory *> factories = ExtensionSystem::PluginManager::instance()->getObjects<Qt4BaseTargetFactory>();
+ foreach (Qt4BaseTargetFactory *factory, factories) {
+ foreach (const QString &id, factory->supportedTargetIds()) {
+ QList<BuildConfigurationInfo> infos
+ = factory->availableBuildConfigurations(id, rootProjectNode()->path(),
+ QtSupport::QtVersionNumber(),
+ QtSupport::QtVersionNumber(INT_MAX, INT_MAX, INT_MAX),
+ Core::FeatureSet());
+ if (!platforms.isEmpty()) {
+ QList<BuildConfigurationInfo> filtered;
+ foreach (const BuildConfigurationInfo &info, infos) {
+ foreach (const QString &platform, platforms) {
+ if (info.version()->supportsPlatform(platform)) {
+ filtered << info;
+ break;
+ }
+ }
+ }
+ infos = filtered;
+ }
+
+ if (!infos.isEmpty())
+ addTarget(factory->create(this, id, infos));
+ }
+ }
+ ProjectExplorer::ProjectExplorerPlugin::instance()->requestProjectModeUpdate(this);
+}
+
/*!
Handle special case were a subproject of the qt directory is opened, and
qt was configured to be built as a shadow build -> also build in the sub-
diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h
index 4b57c4f1e5..a22ab931a2 100644
--- a/src/plugins/qt4projectmanager/qt4project.h
+++ b/src/plugins/qt4projectmanager/qt4project.h
@@ -95,9 +95,6 @@ public:
Qt4BaseTarget *activeTarget() const;
- QList<Core::IDocument *> dependencies(); //NBS remove
- QList<ProjectExplorer::Project *>dependsOn();
-
ProjectExplorer::ProjectNode *rootProjectNode() const;
Qt4ProFileNode *rootQt4ProjectNode() const;
bool validParse(const QString &proFilePath) const;
@@ -140,6 +137,8 @@ public:
bool needsConfiguration() const;
+ void configureAsExampleProject(const QStringList &platforms);
+
signals:
void proParsingDone();
void proFileUpdated(Qt4ProjectManager::Qt4ProFileNode *node, bool, bool);
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
index 3568058a69..410e0b48ee 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
@@ -63,7 +63,6 @@ const char BUILDSUBDIR[] = "Qt4Builder.BuildSubDir";
const char REBUILDSUBDIR[] = "Qt4Builder.RebuildSubDir";
const char CLEANSUBDIR[] = "Qt4Builder.CleanSubDir";
const char ADDLIBRARY[] = "Qt4.AddLibrary";
-const char JUMP_TO_FILE[] = "Qt4.JumpToFile";
const char SEPARATOR[] = "Qt4.Separator";
// Tasks
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
index 789c069921..5386769104 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
@@ -129,7 +129,8 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString *
TextEditor::TextEditorActionHandler *editorHandler
= new TextEditor::TextEditorActionHandler(Constants::C_PROFILEEDITOR,
- TextEditor::TextEditorActionHandler::UnCommentSelection);
+ TextEditor::TextEditorActionHandler::UnCommentSelection
+ | TextEditor::TextEditorActionHandler::JumpToFileUnderCursor);
m_proFileEditorFactory = new ProFileEditorFactory(m_qt4ProjectManager, editorHandler);
addObject(m_proFileEditorFactory);
@@ -238,12 +239,7 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString *
Core::Context proFileEditorContext = Core::Context(Qt4ProjectManager::Constants::C_PROFILEEDITOR);
- QAction *jumpToFile = new QAction(tr("Jump to File Under Cursor"), this);
- command = am->registerAction(jumpToFile,
- Constants::JUMP_TO_FILE, proFileEditorContext);
- command->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
- connect(jumpToFile, SIGNAL(triggered()),
- this, SLOT(jumpToFile()));
+ command = am->command(TextEditor::Constants::JUMP_TO_FILE_UNDER_CURSOR);
contextMenu->addAction(command);
m_addLibraryAction = new QAction(tr("Add Library..."), this);
@@ -369,12 +365,4 @@ void Qt4ProjectManagerPlugin::buildStateChanged(ProjectExplorer::Project *pro)
updateRunQMakeAction();
}
-void Qt4ProjectManagerPlugin::jumpToFile()
-{
- Core::EditorManager *em = Core::EditorManager::instance();
- ProFileEditorWidget *editor = qobject_cast<ProFileEditorWidget*>(em->currentEditor()->widget());
- if (editor)
- editor->jumpToFile();
-}
-
Q_EXPORT_PLUGIN(Qt4ProjectManagerPlugin)
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
index c0bbe0ce3a..f7c2d5995d 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
@@ -59,6 +59,7 @@ class ProFileEditorFactory;
class Qt4ProjectManagerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Qt4ProjectManager.json")
public:
Qt4ProjectManagerPlugin();
@@ -74,7 +75,6 @@ private slots:
void updateRunQMakeAction();
void currentNodeChanged(ProjectExplorer::Node *node);
void buildStateChanged(ProjectExplorer::Project *pro);
- void jumpToFile();
#ifdef WITH_TESTS
void testAbldOutputParsers_data();
diff --git a/src/plugins/qt4projectmanager/qtmodulesinfo.cpp b/src/plugins/qt4projectmanager/qtmodulesinfo.cpp
index a7e6bf3d2d..520afe0f8e 100644
--- a/src/plugins/qt4projectmanager/qtmodulesinfo.cpp
+++ b/src/plugins/qt4projectmanager/qtmodulesinfo.cpp
@@ -47,10 +47,7 @@ struct item
bool isDefault;
};
-typedef QVector<const item*> itemVectorType;
-typedef QHash<QString, const item*> itemHashType;
-
-const itemVectorType itemVector()
+static inline QVector<const item*> itemVector()
{
static const struct item items[] = {
{"core",
@@ -119,37 +116,37 @@ const itemVectorType itemVector()
false}
};
const int itemsCount = sizeof items / sizeof items[0];
- itemVectorType result;
+ QVector<const item*> result;
result.reserve(itemsCount);
for (int i = 0; i < itemsCount; i++)
result.append(items + i);
return result;
}
-Q_GLOBAL_STATIC_WITH_INITIALIZER(itemVectorType, staticItemVector, {
- *x = itemVector();
-})
+class StaticQtModuleInfo
+{
+public:
+ StaticQtModuleInfo() : items(itemVector()) {}
-Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, staticModulesList, {
- const itemVectorType * const itemVector = staticItemVector();
- for (int i = 0; i < itemVector->count(); i++)
- x->append(QString::fromLatin1(itemVector->at(i)->config));
-})
+ const QVector<const item*> items;
+};
-Q_GLOBAL_STATIC_WITH_INITIALIZER(itemHashType, staticItemHash, {
- const itemVectorType * const itemVector = staticItemVector();
- for (int i = 0; i < itemVector->count(); i++)
- x->insert(QString::fromLatin1(itemVector->at(i)->config), itemVector->at(i));
-})
+Q_GLOBAL_STATIC(StaticQtModuleInfo, staticQtModuleInfo)
QStringList QtModulesInfo::modules()
{
- return *staticModulesList();
+ QStringList result;
+ foreach (const item *i, staticQtModuleInfo()->items)
+ result.push_back(i->config);
+ return result;
}
static inline const item *itemForModule(const QString &module)
{
- return staticItemHash()->value(module);
+ foreach (const item *i, staticQtModuleInfo()->items)
+ if (i->config == module)
+ return i;
+ return 0;
}
QString QtModulesInfo::moduleName(const QString &module)
diff --git a/src/plugins/qt4projectmanager/unconfiguredprojectpanel.cpp b/src/plugins/qt4projectmanager/unconfiguredprojectpanel.cpp
index 0481e059ff..ad08817780 100644
--- a/src/plugins/qt4projectmanager/unconfiguredprojectpanel.cpp
+++ b/src/plugins/qt4projectmanager/unconfiguredprojectpanel.cpp
@@ -43,6 +43,7 @@
#include <coreplugin/coreconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/toolchain.h>
#include <QLabel>
@@ -87,8 +88,6 @@ ProjectExplorer::PropertiesPanel *Qt4ProjectManager::Internal::UnconfiguredProje
panel->setIcon(QIcon(":/projectexplorer/images/unconfigured.png"));
TargetSetupPageWrapper *w = new TargetSetupPageWrapper(project);
- connect (w, SIGNAL(projectUpdated(ProjectExplorer::Project*)),
- this, SIGNAL(projectUpdated(ProjectExplorer::Project*)));
panel->setWidget(w);
return panel;
}
@@ -188,7 +187,7 @@ void TargetSetupPageWrapper::keyReleaseEvent(QKeyEvent *event)
void TargetSetupPageWrapper::done()
{
m_targetSetupPage->setupProject(m_project);
- emit projectUpdated(m_project);
+ ProjectExplorer::ProjectExplorerPlugin::instance()->requestProjectModeUpdate(m_project);
Core::ICore::instance()->modeManager()->activateMode(QLatin1String(Core::Constants::MODE_EDIT));
}
diff --git a/src/plugins/qt4projectmanager/unconfiguredprojectpanel.h b/src/plugins/qt4projectmanager/unconfiguredprojectpanel.h
index c8a00cf3cd..6466108f3e 100644
--- a/src/plugins/qt4projectmanager/unconfiguredprojectpanel.h
+++ b/src/plugins/qt4projectmanager/unconfiguredprojectpanel.h
@@ -65,8 +65,6 @@ public:
protected:
void keyReleaseEvent(QKeyEvent *event);
void keyPressEvent(QKeyEvent *event);
-signals:
- void projectUpdated(ProjectExplorer::Project *project);
private slots:
void done();
void noteTextLinkActivated();
diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp
index 3769089ff8..f4c6854eda 100644
--- a/src/plugins/qtsupport/baseqtversion.cpp
+++ b/src/plugins/qtsupport/baseqtversion.cpp
@@ -755,16 +755,7 @@ BaseQtVersion::QmakeBuildConfigs BaseQtVersion::defaultBuildConfig() const
QString BaseQtVersion::qtVersionString() const
{
- if (!m_qtVersionString.isNull())
- return m_qtVersionString;
- m_qtVersionString.clear();
- if (m_qmakeIsExecutable) {
- const QString qmake = qmakeCommand().toString();
- m_qtVersionString =
- ProjectExplorer::DebuggingHelperLibrary::qtVersionForQMake(qmake, &m_qmakeIsExecutable);
- } else {
- qWarning("Cannot determine the Qt version: %s cannot be run.", qPrintable(qmakeCommand().toString()));
- }
+ updateVersionInfo();
return m_qtVersionString;
}
@@ -803,9 +794,6 @@ void BaseQtVersion::updateVersionInfo() const
const QString qtInstallData = m_versionInfo.value(installDataKey);
const QString qtInstallBins = m_versionInfo.value(installBinsKey);
const QString qtHeaderData = m_versionInfo.value(installHeadersKey);
- m_versionInfo.insert(QLatin1String("QMAKE_MKSPECS"),
- QDir::cleanPath(qtInstallData + QLatin1String("/mkspecs")));
-
if (!qtInstallData.isEmpty()) {
m_hasDebuggingHelper = !ProjectExplorer::DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
m_hasQmlDump
@@ -847,6 +835,7 @@ void BaseQtVersion::updateVersionInfo() const
if (fi.exists())
m_hasDemos = true;
}
+ m_qtVersionString = m_versionInfo.value(QLatin1String("QT_VERSION"), QString());
m_versionInfoUpToDate = true;
}
diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp
index c3ea5d3e7d..47fb57bca8 100644
--- a/src/plugins/qtsupport/exampleslistmodel.cpp
+++ b/src/plugins/qtsupport/exampleslistmodel.cpp
@@ -73,6 +73,7 @@ ExamplesListModel::ExamplesListModel(QObject *parent) :
roleNames[IsVideo] = "isVideo";
roleNames[VideoUrl] = "videoUrl";
roleNames[VideoLength] = "videoLength";
+ roleNames[Platforms] = "platforms";
setRoleNames(roleNames);
connect(Core::HelpManager::instance(), SIGNAL(setupFinished()),
@@ -91,6 +92,15 @@ static inline QString fixStringForTags(const QString &string)
return returnString;
}
+static inline QStringList trimStringList(const QStringList &stringlist)
+{
+ QStringList returnList;
+ foreach (const QString &string, stringlist)
+ returnList << string.trimmed();
+
+ return returnList;
+}
+
QList<ExampleItem> ExamplesListModel::parseExamples(QXmlStreamReader* reader, const QString& projectsOffset)
{
QList<ExampleItem> examples;
@@ -117,15 +127,20 @@ QList<ExampleItem> ExamplesListModel::parseExamples(QXmlStreamReader* reader, co
} else if (reader->name() == QLatin1String("dependency")) {
item.dependencies.append(projectsOffset + slash + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("tags")) {
- item.tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','));
+ item.tags = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), QString::SkipEmptyParts));
m_tags.append(item.tags);
- }
+ } else if (reader->name() == QLatin1String("platforms")) {
+ item.platforms = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), QString::SkipEmptyParts));
+ }
break;
case QXmlStreamReader::EndElement:
- if (reader->name() == QLatin1String("example"))
+ if (reader->name() == QLatin1String("example")) {
+ if (item.projectPath.isEmpty() || !QFileInfo(item.projectPath).exists())
+ item.tags.append("broken");
examples.append(item);
- else if (reader->name() == QLatin1String("examples"))
+ } else if (reader->name() == QLatin1String("examples")) {
return examples;
+ }
break;
default: // nothing
break;
@@ -429,6 +444,8 @@ QVariant ExamplesListModel::data(const QModelIndex &index, int role) const
return item.videoUrl;
case VideoLength:
return item.videoLength;
+ case Platforms:
+ return item.platforms;
default:
qDebug() << Q_FUNC_INFO << "role type not supported";
return QVariant();
diff --git a/src/plugins/qtsupport/exampleslistmodel.h b/src/plugins/qtsupport/exampleslistmodel.h
index 75f98e1c1e..c56f4b6ed7 100644
--- a/src/plugins/qtsupport/exampleslistmodel.h
+++ b/src/plugins/qtsupport/exampleslistmodel.h
@@ -43,7 +43,7 @@ namespace Internal {
enum ExampleRoles { Name=Qt::UserRole, ProjectPath, Description, ImageUrl,
DocUrl, FilesToOpen, Tags, Difficulty, HasSourceCode,
- Type, Dependencies, IsVideo, VideoUrl, VideoLength };
+ Type, Dependencies, IsVideo, VideoUrl, VideoLength, Platforms };
enum InstructionalType { Example=0, Demo, Tutorial };
@@ -63,6 +63,7 @@ struct ExampleItem {
bool isVideo;
QString videoUrl;
QString videoLength;
+ QStringList platforms;
};
class ExamplesListModel : public QAbstractListModel {
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
index 86d18d5a8b..af8ef3bd27 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
@@ -35,15 +35,21 @@
#include "exampleslistmodel.h"
#include "screenshotcropper.h"
+#include "qtsupportconstants.h"
+
#include <utils/pathchooser.h>
#include <utils/fileutils.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/coreplugin.h>
+#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
#include <coreplugin/modemanager.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectnodes.h>
#include <QMutex>
#include <QThread>
@@ -67,7 +73,6 @@
#include <QDeclarativeImageProvider>
#include <QDeclarativeEngine>
#include <QDeclarativeContext>
-#include <QtDeclarative>
#include <QDesktopServices>
namespace QtSupport {
@@ -212,7 +217,7 @@ QString GettingStartedWelcomePage::title() const
int GettingStartedWelcomePage::priority() const
{
- return 0;
+ return 4;
}
void GettingStartedWelcomePage::facilitateQml(QDeclarativeEngine *engine)
@@ -220,6 +225,11 @@ void GettingStartedWelcomePage::facilitateQml(QDeclarativeEngine *engine)
m_engine = engine;
}
+GettingStartedWelcomePage::Id GettingStartedWelcomePage::id() const
+{
+ return GettingStarted;
+}
+
ExamplesWelcomePage::ExamplesWelcomePage()
: m_engine(0), m_showExamples(false)
{
@@ -283,6 +293,11 @@ void ExamplesWelcomePage::facilitateQml(QDeclarativeEngine *engine)
rootContenxt->setContextProperty(QLatin1String("gettingStarted"), this);
}
+ExamplesWelcomePage::Id ExamplesWelcomePage::id() const
+{
+ return m_showExamples ? Examples : Tutorials;
+}
+
void ExamplesWelcomePage::openSplitHelp(const QUrl &help)
{
Core::ICore::helpManager()->handleHelpRequest(help.toString()+QLatin1String("?view=split"));
@@ -325,8 +340,8 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI
txt->setBuddy(chooser);
chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory);
QSettings *settings = Core::ICore::settings();
- chooser->setPath(settings->value(
- QString::fromLatin1(C_FALLBACK_ROOT), QDir::homePath()).toString());
+ chooser->setPath(settings->value(QString::fromLatin1(C_FALLBACK_ROOT),
+ Core::DocumentManager::projectsDirectory()).toString());
lay->addWidget(txt, 1, 0);
lay->addWidget(chooser, 1, 1);
QDialogButtonBox *bb = new QDialogButtonBox;
@@ -379,7 +394,7 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI
}
void ExamplesWelcomePage::openProject(const QString &projectFile, const QStringList &additionalFilesToOpen,
- const QUrl &help, const QStringList &dependencies)
+ const QUrl &help, const QStringList &dependencies, const QStringList &platforms)
{
QString proFile = projectFile;
if (proFile.isEmpty())
@@ -387,14 +402,22 @@ void ExamplesWelcomePage::openProject(const QString &projectFile, const QStringL
QStringList filesToOpen = additionalFilesToOpen;
QFileInfo proFileInfo(proFile);
+ if (!proFileInfo.exists())
+ return;
+
// If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail
if (!proFileInfo.isWritable())
proFile = copyToAlternativeLocation(proFileInfo, filesToOpen, dependencies);
// don't try to load help and files if loading the help request is being cancelled
QString errorMessage;
- if (!proFile.isEmpty() && ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile, &errorMessage)) {
+ ProjectExplorer::ProjectExplorerPlugin *peplugin = ProjectExplorer::ProjectExplorerPlugin::instance();
+ if (proFile.isEmpty())
+ return;
+ if (ProjectExplorer::Project *project = peplugin->openProject(proFile, &errorMessage)) {
Core::ICore::openFiles(filesToOpen);
+ if (project->needsConfiguration())
+ project->configureAsExampleProject(platforms);
Core::ModeManager::activateModeType(QLatin1String(Core::Constants::MODE_EDIT_TYPE));
Core::ICore::helpManager()->handleHelpRequest(help.toString()+QLatin1String("?view=split"));
}
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.h b/src/plugins/qtsupport/gettingstartedwelcomepage.h
index aefa4fdeee..1b7855572e 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.h
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.h
@@ -58,6 +58,7 @@ public:
QString title() const;
int priority() const;
void facilitateQml(QDeclarativeEngine *);
+ Id id() const;
private:
QDeclarativeEngine *m_engine;
@@ -76,6 +77,7 @@ public:
int priority() const;
bool hasSearchBar() const;
void facilitateQml(QDeclarativeEngine *);
+ Id id() const;
Q_INVOKABLE QStringList tagList() const;
Q_INVOKABLE void openUrl(const QUrl &url);
@@ -86,7 +88,7 @@ public slots:
void openSplitHelp(const QUrl &help);
void openHelp(const QUrl &help);
void openProject(const QString& projectFile, const QStringList& additionalFilesToOpen,
- const QUrl& help, const QStringList &dependencies);
+ const QUrl& help, const QStringList &dependencies, const QStringList &platforms);
void updateTagsModel();
private:
diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp
index 1964711318..d113448ce3 100644
--- a/src/plugins/qtsupport/qtoptionspage.cpp
+++ b/src/plugins/qtsupport/qtoptionspage.cpp
@@ -101,8 +101,7 @@ QIcon QtOptionsPage::categoryIcon() const
QWidget *QtOptionsPage::createPage(QWidget *parent)
{
- QtVersionManager *vm = QtVersionManager::instance();
- m_widget = new QtOptionsPageWidget(parent, vm->versions());
+ m_widget = new QtOptionsPageWidget(parent);
if (m_searchKeywords.isEmpty())
m_searchKeywords = m_widget->searchKeywords();
return m_widget;
@@ -126,7 +125,7 @@ bool QtOptionsPage::matches(const QString &s) const
//-----------------------------------------------------
-QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *> versions)
+QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent)
: QWidget(parent)
, m_specifyNameString(tr("<specify a name>"))
, m_ui(new Internal::Ui::QtVersionManager())
@@ -135,11 +134,9 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *>
, m_invalidVersionIcon(QLatin1String(":/projectexplorer/images/compile_error.png"))
, m_warningVersionIcon(QLatin1String(":/projectexplorer/images/compile_warning.png"))
, m_configurationWidget(0)
+ , m_autoItem(0)
+ , m_manualItem(0)
{
- // Initialize m_versions
- foreach (BaseQtVersion *version, versions)
- m_versions.push_back(version->clone());
-
QWidget *versionInfoWidget = new QWidget();
m_versionUi->setupUi(versionInfoWidget);
m_versionUi->editPathPushButton->setText(tr(Utils::PathChooser::browseButtonLabel));
@@ -160,26 +157,23 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *>
m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
m_ui->qtdirList->header()->setStretchLastSection(false);
m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
- QTreeWidgetItem *autoItem = new QTreeWidgetItem(m_ui->qtdirList);
+ m_autoItem = new QTreeWidgetItem(m_ui->qtdirList);
m_ui->qtdirList->installEventFilter(this);
- autoItem->setText(0, tr("Auto-detected"));
- autoItem->setFirstColumnSpanned(true);
- autoItem->setFlags(Qt::ItemIsEnabled);
- QTreeWidgetItem *manualItem = new QTreeWidgetItem(m_ui->qtdirList);
- manualItem->setText(0, tr("Manual"));
- manualItem->setFirstColumnSpanned(true);
- manualItem->setFlags(Qt::ItemIsEnabled);
+ m_autoItem->setText(0, tr("Auto-detected"));
+ m_autoItem->setFirstColumnSpanned(true);
+ m_autoItem->setFlags(Qt::ItemIsEnabled);
+ m_manualItem = new QTreeWidgetItem(m_ui->qtdirList);
+ m_manualItem->setText(0, tr("Manual"));
+ m_manualItem->setFirstColumnSpanned(true);
+ m_manualItem->setFlags(Qt::ItemIsEnabled);
+
+ QList<int> additions;
+ QList<BaseQtVersion *> versions = QtVersionManager::instance()->versions();
+ foreach (BaseQtVersion *v, versions)
+ additions.append(v->uniqueId());
+
+ updateQtVersions(additions, QList<int>(), QList<int>());
- for (int i = 0; i < m_versions.count(); ++i) {
- BaseQtVersion *version = m_versions.at(i);
- QTreeWidgetItem *item = new QTreeWidgetItem(version->isAutodetected()? autoItem : manualItem);
- item->setText(0, version->displayName());
- item->setText(1, version->qmakeCommand().toUserOutput());
- item->setData(0, VersionIdRole, version->uniqueId());
- item->setData(0, ToolChainIdRole, defaultToolChainId(version));
- const ValidityInfo info = validInformation(version);
- item->setIcon(0, info.icon);
- }
m_ui->qtdirList->expandAll();
connect(m_versionUi->nameEdit, SIGNAL(textEdited(QString)),
@@ -219,6 +213,9 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *>
connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
this, SLOT(qtVersionsDumpUpdated(Utils::FileName)));
+ connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
+ this, SLOT(updateQtVersions(QList<int>,QList<int>,QList<int>)));
+
connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
this, SLOT(toolChainsUpdated()));
}
@@ -567,6 +564,73 @@ void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentIt
dialog->show();
}
+void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QList<int> &removals,
+ const QList<int> &changes)
+{
+ QtVersionManager *vm = QtVersionManager::instance();
+
+ QList<QTreeWidgetItem *> toRemove;
+ QList<int> toAdd = additions;
+
+ // Generate list of all existing items:
+ QList<QTreeWidgetItem *> itemList;
+ for (int i = 0; i < m_autoItem->childCount(); ++i)
+ itemList.append(m_autoItem->child(i));
+ for (int i = 0; i < m_manualItem->childCount(); ++i)
+ itemList.append(m_manualItem->child(i));
+
+ // Find existing items to remove/change:
+ foreach (QTreeWidgetItem *item, itemList) {
+ int id = item->data(0, VersionIdRole).toInt();
+ if (removals.contains(id)) {
+ toRemove.append(item);
+ continue;
+ }
+
+ if (changes.contains(id)) {
+ toAdd.append(id);
+ toRemove.append(item);
+ continue;
+ }
+ }
+
+ // Remove changed/removed items:
+ foreach (QTreeWidgetItem *item, toRemove) {
+ int index = indexForTreeItem(item);
+ delete m_versions.at(index);
+ m_versions.removeAt(index);
+ delete item;
+ }
+
+ // Add changed/added items:
+ foreach (int a, toAdd) {
+ BaseQtVersion *version = vm->version(a)->clone();
+ m_versions.append(version);
+ QTreeWidgetItem *item = new QTreeWidgetItem;
+
+ item->setText(0, version->displayName());
+ item->setText(1, version->qmakeCommand().toUserOutput());
+ item->setData(0, VersionIdRole, version->uniqueId());
+ item->setData(0, ToolChainIdRole, defaultToolChainId(version));
+ const ValidityInfo info = validInformation(version);
+ item->setIcon(0, info.icon);
+
+ // Insert in the right place:
+ QTreeWidgetItem *parent = version->isAutodetected()? m_autoItem : m_manualItem;
+ for (int i = 0; i < parent->childCount(); ++i) {
+ BaseQtVersion *currentVersion = m_versions.at(indexForTreeItem(parent->child(i)));
+ if (currentVersion->qtVersion() > version->qtVersion())
+ continue;
+ parent->insertChild(i, item);
+ parent = 0;
+ break;
+ }
+
+ if (parent)
+ parent->addChild(item);
+ }
+}
+
QtOptionsPageWidget::~QtOptionsPageWidget()
{
delete m_ui;
diff --git a/src/plugins/qtsupport/qtoptionspage.h b/src/plugins/qtsupport/qtoptionspage.h
index f09ca28c39..ef854bc57e 100644
--- a/src/plugins/qtsupport/qtoptionspage.h
+++ b/src/plugins/qtsupport/qtoptionspage.h
@@ -64,7 +64,7 @@ class QtOptionsPageWidget : public QWidget
Q_OBJECT
public:
- QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *> versions);
+ QtOptionsPageWidget(QWidget *parent);
~QtOptionsPageWidget();
QList<BaseQtVersion *> versions() const;
void finish();
@@ -97,6 +97,7 @@ private:
QtConfigWidget *m_configurationWidget;
private slots:
+ void updateQtVersions(const QList<int> &, const QList<int> &, const QList<int> &);
void qtVersionChanged();
void versionChanged(QTreeWidgetItem *item, QTreeWidgetItem *old);
void addQtDir();
@@ -129,6 +130,9 @@ private:
ValidityInfo validInformation(const BaseQtVersion *version);
QList<ProjectExplorer::ToolChain*> toolChains(const BaseQtVersion *version);
QString defaultToolChainId(const BaseQtVersion *verison);
+
+ QTreeWidgetItem *m_autoItem;
+ QTreeWidgetItem *m_manualItem;
};
class QtOptionsPage : public Core::IOptionsPage
diff --git a/src/plugins/qtsupport/qtoutputformatter.cpp b/src/plugins/qtsupport/qtoutputformatter.cpp
index a34316ca3c..2fb33a43c6 100644
--- a/src/plugins/qtsupport/qtoutputformatter.cpp
+++ b/src/plugins/qtsupport/qtoutputformatter.cpp
@@ -53,6 +53,7 @@ QtOutputFormatter::QtOutputFormatter(ProjectExplorer::Project *project)
":")) // colon
, m_qtError(QLatin1String("Object::.*in (.*:\\d+)"))
, m_qtAssert(QLatin1String("ASSERT: .* in file (.+, line \\d+)"))
+ , m_qtAssertX(QLatin1String("ASSERT failure in .*: \".*\", file (.+, line \\d+)"))
, m_qtTestFail(QLatin1String("^ Loc: \\[(.*)\\]"))
, m_project(project)
{
@@ -83,6 +84,10 @@ LinkResult QtOutputFormatter::matchLine(const QString &line) const
lr.href = m_qtAssert.cap(1);
lr.start = m_qtAssert.pos(1);
lr.end = lr.start + lr.href.length();
+ } else if (m_qtAssertX.indexIn(line) != -1) {
+ lr.href = m_qtAssertX.cap(1);
+ lr.start = m_qtAssertX.pos(1);
+ lr.end = lr.start + lr.href.length();
} else if (m_qtTestFail.indexIn(line) != -1) {
lr.href = m_qtTestFail.cap(1);
lr.start = m_qtTestFail.pos(1);
@@ -97,9 +102,6 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
cursor.movePosition(QTextCursor::End);
cursor.beginEditBlock();
- QString text = txt;
- text.remove(QLatin1Char('\r'));
-
QString deferedText;
int start = 0;
diff --git a/src/plugins/qtsupport/qtoutputformatter.h b/src/plugins/qtsupport/qtoutputformatter.h
index 762f099bf6..b9c7ccf8bf 100644
--- a/src/plugins/qtsupport/qtoutputformatter.h
+++ b/src/plugins/qtsupport/qtoutputformatter.h
@@ -78,10 +78,10 @@ private:
QRegExp m_qmlError;
QRegExp m_qtError;
QRegExp m_qtAssert;
+ QRegExp m_qtAssertX;
QRegExp m_qtTestFail;
QWeakPointer<ProjectExplorer::Project> m_project;
QString m_lastLine;
- QString m_deferedText;
Utils::FileInProjectFinder m_projectFinder;
};
diff --git a/src/plugins/qtsupport/qtsupport.pro b/src/plugins/qtsupport/qtsupport.pro
index 088a61e7d0..383c154a42 100644
--- a/src/plugins/qtsupport/qtsupport.pro
+++ b/src/plugins/qtsupport/qtsupport.pro
@@ -1,7 +1,13 @@
TEMPLATE = lib
TARGET = QtSupport
DEFINES += QT_CREATOR QTSUPPORT_LIBRARY
-QT += network declarative
+QT += network
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
+
include(../../qtcreatorplugin.pri)
include(qtsupport_dependencies.pri)
DEFINES += \
diff --git a/src/plugins/qtsupport/qtsupportplugin.h b/src/plugins/qtsupport/qtsupportplugin.h
index 8ea327febd..0400e313e9 100644
--- a/src/plugins/qtsupport/qtsupportplugin.h
+++ b/src/plugins/qtsupport/qtsupportplugin.h
@@ -45,6 +45,7 @@ class ExamplesWelcomePage;
class QtSupportPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QtSupport.json")
public:
bool initialize(const QStringList &arguments, QString *errorMessage);
diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp
index 185023e851..b1472aad82 100644
--- a/src/plugins/qtsupport/qtversionmanager.cpp
+++ b/src/plugins/qtsupport/qtversionmanager.cpp
@@ -48,6 +48,7 @@
#include <extensionsystem/pluginmanager.h>
+#include <utils/filesystemwatcher.h>
#include <utils/persistentsettings.h>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
@@ -98,6 +99,13 @@ static T *createToolChain(const QString &id)
return 0;
}
+static QString globalSettingsFileName()
+{
+ ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
+ return QFileInfo(pm->globalSettings()->fileName()).absolutePath()
+ + QLatin1String(QTVERSION_FILENAME);
+}
+
static QString settingsFileName()
{
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
@@ -117,7 +125,8 @@ bool qtVersionNumberCompare(BaseQtVersion *a, BaseQtVersion *b)
// --------------------------------------------------------------------------
QtVersionManager *QtVersionManager::m_self = 0;
-QtVersionManager::QtVersionManager()
+QtVersionManager::QtVersionManager() :
+ m_configFileWatcher(0)
{
m_self = this;
m_idcount = 1;
@@ -139,6 +148,15 @@ void QtVersionManager::extensionsInitialized()
}
saveQtVersions();
+
+ const QString configFileName = globalSettingsFileName();
+ if (QFileInfo(configFileName).exists()) {
+ m_configFileWatcher = new Utils::FileSystemWatcher(this);
+ connect(m_configFileWatcher, SIGNAL(fileChanged(QString)),
+ this, SLOT(updateFromInstaller()));
+ m_configFileWatcher->addFile(configFileName,
+ Utils::FileSystemWatcher::WatchModifiedDate);
+ } // exists
}
bool QtVersionManager::delayedInitialize()
@@ -211,11 +229,17 @@ bool QtVersionManager::restoreQtVersions()
void QtVersionManager::updateFromInstaller()
{
+ // Handle overwritting of data:
+ if (m_configFileWatcher) {
+ const QString path = globalSettingsFileName();
+ m_configFileWatcher->removeFile(path);
+ m_configFileWatcher->addFile(path, Utils::FileSystemWatcher::WatchModifiedDate);
+ }
+
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
QList<QtVersionFactory *> factories = pm->getObjects<QtVersionFactory>();
Utils::PersistentSettingsReader reader;
- if (!reader.load(QFileInfo(pm->globalSettings()->fileName()).absolutePath()
- + QLatin1String(QTVERSION_FILENAME)))
+ if (!reader.load(globalSettingsFileName()))
return;
QVariantMap data = reader.restoreValues();
diff --git a/src/plugins/qtsupport/qtversionmanager.h b/src/plugins/qtsupport/qtversionmanager.h
index d192212256..7a95a5fe51 100644
--- a/src/plugins/qtsupport/qtversionmanager.h
+++ b/src/plugins/qtsupport/qtversionmanager.h
@@ -39,6 +39,8 @@
#include <QSet>
#include <QStringList>
+namespace Utils { class FileSystemWatcher; }
+
namespace QtSupport {
namespace Internal {
class QtOptionsPage;
@@ -113,6 +115,9 @@ signals:
public slots:
void updateDumpFor(const Utils::FileName &qmakeCommand);
+private slots:
+ void updateFromInstaller();
+
private:
// This function is really simplistic...
static bool equals(BaseQtVersion *a, BaseQtVersion *b);
@@ -127,7 +132,6 @@ private:
bool restoreQtVersions();
bool legacyRestore();
void findSystemQt();
- void updateFromInstaller();
void saveQtVersions();
void updateDocumentation();
// Used by QtOptionsPage
@@ -147,6 +151,8 @@ private:
// Compatibility with pre-2.2:
QStringList m_pendingMwcUpdates;
QStringList m_pendingGcceUpdates;
+
+ Utils::FileSystemWatcher *m_configFileWatcher;
};
namespace Internal {
diff --git a/src/plugins/qtsupport/screenshotcropper.cpp b/src/plugins/qtsupport/screenshotcropper.cpp
index 0209cbc370..59d1585e9b 100644
--- a/src/plugins/qtsupport/screenshotcropper.cpp
+++ b/src/plugins/qtsupport/screenshotcropper.cpp
@@ -43,13 +43,20 @@
namespace QtSupport {
namespace Internal {
+class AreasOfInterest {
+public:
+ AreasOfInterest();
+ QMap<QString, QRect> areas;
+};
+
+AreasOfInterest::AreasOfInterest()
+{
#ifdef QT_CREATOR
-Q_GLOBAL_STATIC_WITH_INITIALIZER(AreasOfInterest, areasOfInterest, {
- *x = ScreenshotCropper::loadAreasOfInterest(Core::ICore::resourcePath() + QLatin1String("/welcomescreen/images_areaofinterest.xml"));
-})
-#else
-Q_GLOBAL_STATIC(AreasOfInterest, areasOfInterest)
+ areas = ScreenshotCropper::loadAreasOfInterest(Core::ICore::resourcePath() + QLatin1String("/welcomescreen/images_areaofinterest.xml"));
#endif // QT_CREATOR
+}
+
+Q_GLOBAL_STATIC(AreasOfInterest, welcomeScreenAreas)
static inline QString fileNameForPath(const QString &path)
{
@@ -81,7 +88,7 @@ static QRect cropRectForAreaOfInterest(const QSize &imageSize, const QSize &crop
QImage ScreenshotCropper::croppedImage(const QImage &sourceImage, const QString &filePath, const QSize &cropSize)
{
- const QRect areaOfInterest = areasOfInterest()->value(fileNameForPath(filePath));
+ const QRect areaOfInterest = welcomeScreenAreas()->areas.value(fileNameForPath(filePath));
if (areaOfInterest.isValid()) {
const QRect cropRect = cropRectForAreaOfInterest(sourceImage.size(), cropSize, areaOfInterest);
@@ -113,9 +120,9 @@ static const QString xmlAttributeY = QLatin1String("y");
static const QString xmlAttributeWidth = QLatin1String("width");
static const QString xmlAttributeHeight = QLatin1String("height");
-AreasOfInterest ScreenshotCropper::loadAreasOfInterest(const QString &areasXmlFile)
+QMap<QString, QRect> ScreenshotCropper::loadAreasOfInterest(const QString &areasXmlFile)
{
- AreasOfInterest areasOfInterest;
+ QMap<QString, QRect> areasOfInterest;
QFile xmlFile(areasXmlFile);
if (!xmlFile.open(QIODevice::ReadOnly)) {
qWarning() << Q_FUNC_INFO << "Could not open file" << areasXmlFile;
@@ -144,7 +151,7 @@ AreasOfInterest ScreenshotCropper::loadAreasOfInterest(const QString &areasXmlFi
return areasOfInterest;
}
-bool ScreenshotCropper::saveAreasOfInterest(const QString &areasXmlFile, AreasOfInterest &areas)
+bool ScreenshotCropper::saveAreasOfInterest(const QString &areasXmlFile, QMap<QString, QRect> &areas)
{
QFile file(areasXmlFile);
if (!file.open(QIODevice::WriteOnly))
diff --git a/src/plugins/qtsupport/screenshotcropper.h b/src/plugins/qtsupport/screenshotcropper.h
index a3e52c9594..5ee91b42eb 100644
--- a/src/plugins/qtsupport/screenshotcropper.h
+++ b/src/plugins/qtsupport/screenshotcropper.h
@@ -40,14 +40,12 @@
namespace QtSupport {
namespace Internal {
-typedef QMap<QString, QRect> AreasOfInterest;
-
class ScreenshotCropper
{
public:
static QImage croppedImage(const QImage &sourceImage, const QString &filePath, const QSize &cropSize);
- static AreasOfInterest loadAreasOfInterest(const QString &areasXmlFile);
- static bool saveAreasOfInterest(const QString &areasXmlFile, AreasOfInterest &areas);
+ static QMap<QString, QRect> loadAreasOfInterest(const QString &areasXmlFile);
+ static bool saveAreasOfInterest(const QString &areasXmlFile, QMap<QString, QRect> &areas);
};
} // namespace Internal
diff --git a/src/plugins/remotelinux/abstractembeddedlinuxtarget.cpp b/src/plugins/remotelinux/abstractembeddedlinuxtarget.cpp
index 16985e39a7..78f7eb775b 100644
--- a/src/plugins/remotelinux/abstractembeddedlinuxtarget.cpp
+++ b/src/plugins/remotelinux/abstractembeddedlinuxtarget.cpp
@@ -41,13 +41,11 @@ using namespace Qt4ProjectManager;
namespace RemoteLinux {
-AbstractEmbeddedLinuxTarget::AbstractEmbeddedLinuxTarget(Qt4Project *parent, const QString &id,
- const QString &supportedOsType) :
+AbstractEmbeddedLinuxTarget::AbstractEmbeddedLinuxTarget(Qt4Project *parent, const QString &id) :
Qt4BaseTarget(parent, id),
m_buildConfigurationFactory(new Qt4BuildConfigurationFactory(this)),
- m_supportedOsType(supportedOsType),
m_deploymentInfo(new DeploymentInfo(this)),
- m_deviceConfigModel(new Internal::TypeSpecificDeviceConfigurationListModel(supportedOsType, this))
+ m_deviceConfigModel(new Internal::TypeSpecificDeviceConfigurationListModel(this))
{
}
diff --git a/src/plugins/remotelinux/abstractembeddedlinuxtarget.h b/src/plugins/remotelinux/abstractembeddedlinuxtarget.h
index da0e26efea..f6ff1fe8d7 100644
--- a/src/plugins/remotelinux/abstractembeddedlinuxtarget.h
+++ b/src/plugins/remotelinux/abstractembeddedlinuxtarget.h
@@ -36,9 +36,13 @@
#include <qt4projectmanager/qt4target.h>
+#include <QSharedPointer>
#include <QString>
-namespace ProjectExplorer { class IBuildConfigurationFactory; }
+namespace ProjectExplorer {
+class IBuildConfigurationFactory;
+class IDevice;
+}
namespace Qt4ProjectManager { class Qt4BuildConfigurationFactory; }
namespace RemoteLinux {
@@ -49,20 +53,21 @@ class REMOTELINUX_EXPORT AbstractEmbeddedLinuxTarget : public Qt4ProjectManager:
{
Q_OBJECT
public:
- AbstractEmbeddedLinuxTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id,
- const QString &supportedOsType);
+ AbstractEmbeddedLinuxTarget(Qt4ProjectManager::Qt4Project *parent, const QString &id);
ProjectExplorer::IBuildConfigurationFactory *buildConfigurationFactory() const;
- QString supportedOsType() const { return m_supportedOsType; }
DeploymentInfo *deploymentInfo() const { return m_deploymentInfo; }
Internal::TypeSpecificDeviceConfigurationListModel *deviceConfigModel() const {
return m_deviceConfigModel;
}
+ virtual bool supportsDevice(const QSharedPointer<const ProjectExplorer::IDevice> &device) const = 0;
+
+signals:
+ void supportedDevicesChanged();
private:
Qt4ProjectManager::Qt4BuildConfigurationFactory * const m_buildConfigurationFactory;
- const QString m_supportedOsType;
DeploymentInfo * const m_deploymentInfo;
Internal::TypeSpecificDeviceConfigurationListModel * const m_deviceConfigModel;
};
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp
index 80be33ea36..33433d61f8 100644
--- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp
+++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp
@@ -304,7 +304,7 @@ void AbstractRemoteLinuxDeployService::handleConnectionFailure()
break;
case Connecting: {
QString errorMsg = tr("Could not connect to host: %1").arg(d->connection->errorString());
- if (deviceConfiguration()->deviceType() == LinuxDeviceConfiguration::Emulator)
+ if (deviceConfiguration()->machineType() == LinuxDeviceConfiguration::Emulator)
errorMsg += tr("\nDid the emulator fail to start?");
else
errorMsg += tr("\nIs the device connected and set up for network access?");
diff --git a/src/plugins/remotelinux/deployablefilesperprofile.cpp b/src/plugins/remotelinux/deployablefilesperprofile.cpp
index 8cfb982aa5..328cf5d51e 100644
--- a/src/plugins/remotelinux/deployablefilesperprofile.cpp
+++ b/src/plugins/remotelinux/deployablefilesperprofile.cpp
@@ -74,7 +74,7 @@ public:
using namespace Internal;
DeployableFilesPerProFile::DeployableFilesPerProFile(const Qt4ProFileNode *proFileNode,
- QObject *parent)
+ const QString &installPrefix, QObject *parent)
: QAbstractTableModel(parent), d(new DeployableFilesPerProFilePrivate(proFileNode))
{
if (d->projectType == ApplicationTemplate) {
@@ -90,6 +90,11 @@ DeployableFilesPerProFile::DeployableFilesPerProFile(const Qt4ProFileNode *proFi
foreach (const QString &file, elem.files)
d->deployables << DeployableFile(file, elem.path);
}
+
+ if (!installPrefix.isEmpty()) {
+ for (int i = 0; i < d->deployables.count(); ++i)
+ d->deployables[i].remoteDir.prepend(installPrefix + QLatin1Char('/'));
+ }
}
DeployableFilesPerProFile::~DeployableFilesPerProFile()
@@ -133,7 +138,7 @@ QVariant DeployableFilesPerProFile::data(const QModelIndex &index, int role) con
if (index.column() == 0 && role == Qt::DisplayRole)
return QDir::toNativeSeparators(d.localFilePath);
if (role == Qt::DisplayRole || role == Qt::EditRole)
- return d.remoteDir;
+ return QDir::cleanPath(d.remoteDir);
return QVariant();
}
diff --git a/src/plugins/remotelinux/deployablefilesperprofile.h b/src/plugins/remotelinux/deployablefilesperprofile.h
index 5fd9695995..c219f0743c 100644
--- a/src/plugins/remotelinux/deployablefilesperprofile.h
+++ b/src/plugins/remotelinux/deployablefilesperprofile.h
@@ -53,7 +53,7 @@ class REMOTELINUX_EXPORT DeployableFilesPerProFile : public QAbstractTableModel
Q_OBJECT
public:
DeployableFilesPerProFile(const Qt4ProjectManager::Qt4ProFileNode *proFileNode,
- QObject *parent);
+ const QString &installPrefix, QObject *parent);
~DeployableFilesPerProFile();
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
diff --git a/src/plugins/remotelinux/deploymentinfo.cpp b/src/plugins/remotelinux/deploymentinfo.cpp
index 42c31dff6d..9ab125493f 100644
--- a/src/plugins/remotelinux/deploymentinfo.cpp
+++ b/src/plugins/remotelinux/deploymentinfo.cpp
@@ -53,17 +53,17 @@ public:
QList<DeployableFilesPerProFile *> listModels;
const AbstractEmbeddedLinuxTarget * const target;
+ QString installPrefix;
};
} // namespace Internal
using namespace Internal;
-DeploymentInfo::DeploymentInfo(AbstractEmbeddedLinuxTarget *target) :
- QAbstractListModel(target),
- d(new DeploymentInfoPrivate(target))
+DeploymentInfo::DeploymentInfo(AbstractEmbeddedLinuxTarget *target, const QString &installPrefix)
+ : QAbstractListModel(target), d(new DeploymentInfoPrivate(target))
{
connect (d->target->qt4Project(), SIGNAL(proParsingDone()), SLOT(createModels()));
- createModels();
+ setInstallPrefix(installPrefix);
}
DeploymentInfo::~DeploymentInfo()
@@ -102,7 +102,7 @@ void DeploymentInfo::createModels(const Qt4ProFileNode *proFileNode)
case ApplicationTemplate:
case LibraryTemplate:
case AuxTemplate:
- d->listModels << new DeployableFilesPerProFile(proFileNode, this);
+ d->listModels << new DeployableFilesPerProFile(proFileNode, d->installPrefix, this);
break;
case SubDirsTemplate: {
const QList<Qt4PriFileNode *> &subProjects = proFileNode->subProjectNodesExact();
@@ -133,6 +133,12 @@ bool DeploymentInfo::isModified() const
return false;
}
+void DeploymentInfo::setInstallPrefix(const QString &installPrefix)
+{
+ d->installPrefix = installPrefix;
+ createModels();
+}
+
int DeploymentInfo::deployableCount() const
{
int count = 0;
diff --git a/src/plugins/remotelinux/deploymentinfo.h b/src/plugins/remotelinux/deploymentinfo.h
index 5690b3f7c2..fffa093ac0 100644
--- a/src/plugins/remotelinux/deploymentinfo.h
+++ b/src/plugins/remotelinux/deploymentinfo.h
@@ -51,11 +51,12 @@ class REMOTELINUX_EXPORT DeploymentInfo : public QAbstractListModel
{
Q_OBJECT
public:
- DeploymentInfo(AbstractEmbeddedLinuxTarget *target);
+ DeploymentInfo(AbstractEmbeddedLinuxTarget *target, const QString &installPrefix = QString());
~DeploymentInfo();
void setUnmodified();
bool isModified() const;
+ void setInstallPrefix(const QString &installPrefix);
int deployableCount() const;
DeployableFile deployableAt(int i) const;
QString remoteExecutableFilePath(const QString &localExecutableFilePath) const;
diff --git a/src/plugins/remotelinux/genericembeddedlinuxtarget.cpp b/src/plugins/remotelinux/genericembeddedlinuxtarget.cpp
index a20ed770fc..db0371b7a2 100644
--- a/src/plugins/remotelinux/genericembeddedlinuxtarget.cpp
+++ b/src/plugins/remotelinux/genericembeddedlinuxtarget.cpp
@@ -36,6 +36,7 @@
#include "remotelinuxrunconfiguration.h"
#include <projectexplorer/customexecutablerunconfiguration.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
#include <qt4projectmanager/qt4nodes.h>
#include <qt4projectmanager/qt4project.h>
@@ -47,8 +48,7 @@ namespace RemoteLinux {
namespace Internal {
GenericEmbeddedLinuxTarget::GenericEmbeddedLinuxTarget(Qt4ProjectManager::Qt4Project *parent,
- const QString &id) :
- AbstractEmbeddedLinuxTarget(parent, id, QLatin1String(Constants::GenericLinuxOsType))
+ const QString &id) : AbstractEmbeddedLinuxTarget(parent, id)
{
setDisplayName(tr("Embedded Linux"));
}
@@ -71,6 +71,11 @@ Utils::FileName GenericEmbeddedLinuxTarget::mkspec(const Qt4ProjectManager::Qt4B
return version->mkspec();
}
+bool GenericEmbeddedLinuxTarget::supportsDevice(const ProjectExplorer::IDevice::ConstPtr &device) const
+{
+ return device->type() == QLatin1String(Constants::GenericLinuxOsType);
+}
+
void GenericEmbeddedLinuxTarget::createApplicationProFiles(bool reparse)
{
if (!reparse)
diff --git a/src/plugins/remotelinux/genericembeddedlinuxtarget.h b/src/plugins/remotelinux/genericembeddedlinuxtarget.h
index 5850f4d142..cdb52f2034 100644
--- a/src/plugins/remotelinux/genericembeddedlinuxtarget.h
+++ b/src/plugins/remotelinux/genericembeddedlinuxtarget.h
@@ -50,6 +50,7 @@ public:
void createApplicationProFiles(bool reparse);
QList<ProjectExplorer::RunConfiguration *> runConfigurationsForNode(ProjectExplorer::Node *n);
Utils::FileName mkspec(const Qt4ProjectManager::Qt4BuildConfiguration *bc) const;
+ bool supportsDevice(const QSharedPointer<const ProjectExplorer::IDevice> &device) const;
private:
friend class EmbeddedLinuxTargetFactory;
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.cpp
index 0c7865199c..625b6bad70 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.cpp
@@ -33,6 +33,7 @@
#include "genericlinuxdeviceconfigurationwizard.h"
#include "genericlinuxdeviceconfigurationwidget.h"
+#include "linuxdeviceconfiguration.h"
#include "linuxdevicetestdialog.h"
#include "publickeydeploymentdialog.h"
#include "remotelinuxprocessesdialog.h"
@@ -41,10 +42,12 @@
#include <utils/qtcassert.h>
+using namespace ProjectExplorer;
+
namespace RemoteLinux {
GenericLinuxDeviceConfigurationFactory::GenericLinuxDeviceConfigurationFactory(QObject *parent)
- : ILinuxDeviceConfigurationFactory(parent)
+ : IDeviceFactory(parent)
{
}
@@ -53,26 +56,35 @@ QString GenericLinuxDeviceConfigurationFactory::displayName() const
return tr("Generic Linux Device");
}
-ILinuxDeviceConfigurationWizard *GenericLinuxDeviceConfigurationFactory::createWizard(QWidget *parent) const
+IDeviceWizard *GenericLinuxDeviceConfigurationFactory::createWizard(QWidget *parent) const
{
return new GenericLinuxDeviceConfigurationWizard(parent);
}
-ILinuxDeviceConfigurationWidget *GenericLinuxDeviceConfigurationFactory::createWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent) const
+IDeviceWidget *GenericLinuxDeviceConfigurationFactory::createWidget(const IDevice::Ptr &device,
+ QWidget *parent) const
{
- return new GenericLinuxDeviceConfigurationWidget(deviceConfig, parent);
+ return new GenericLinuxDeviceConfigurationWidget(device.staticCast<LinuxDeviceConfiguration>(),
+ parent);
}
-bool GenericLinuxDeviceConfigurationFactory::supportsOsType(const QString &osType) const
+IDevice::Ptr GenericLinuxDeviceConfigurationFactory::loadDevice(const QVariantMap &map) const
{
- return osType == QLatin1String(Constants::GenericLinuxOsType);
+ QTC_ASSERT(supportsDeviceType(IDevice::typeFromMap(map)),
+ return LinuxDeviceConfiguration::Ptr());
+ LinuxDeviceConfiguration::Ptr device = LinuxDeviceConfiguration::create();
+ device->fromMap(map);
+ return device;
}
-QString GenericLinuxDeviceConfigurationFactory::displayNameForOsType(const QString &osType) const
+bool GenericLinuxDeviceConfigurationFactory::supportsDeviceType(const QString &deviceType) const
{
- QTC_ASSERT(supportsOsType(osType), return QString());
+ return deviceType == QLatin1String(Constants::GenericLinuxOsType);
+}
+
+QString GenericLinuxDeviceConfigurationFactory::displayNameForDeviceType(const QString &deviceType) const
+{
+ QTC_ASSERT(supportsDeviceType(deviceType), return QString());
return tr("Generic Linux");
}
@@ -85,7 +97,7 @@ QStringList GenericLinuxDeviceConfigurationFactory::supportedDeviceActionIds() c
QString GenericLinuxDeviceConfigurationFactory::displayNameForActionId(const QString &actionId) const
{
- Q_ASSERT(supportedDeviceActionIds().contains(actionId));
+ QTC_ASSERT(supportedDeviceActionIds().contains(actionId), return QString());
if (actionId == QLatin1String(Constants::GenericTestDeviceActionId))
return tr("Test");
@@ -97,18 +109,18 @@ QString GenericLinuxDeviceConfigurationFactory::displayNameForActionId(const QSt
}
QDialog *GenericLinuxDeviceConfigurationFactory::createDeviceAction(const QString &actionId,
- const LinuxDeviceConfiguration::ConstPtr &deviceConfig, QWidget *parent) const
+ const IDevice::ConstPtr &device, QWidget *parent) const
{
- Q_ASSERT(supportedDeviceActionIds().contains(actionId));
+ QTC_ASSERT(supportedDeviceActionIds().contains(actionId), return 0);
+ const LinuxDeviceConfiguration::ConstPtr lDevice
+ = device.staticCast<const LinuxDeviceConfiguration>();
if (actionId == QLatin1String(Constants::GenericTestDeviceActionId))
- return new LinuxDeviceTestDialog(deviceConfig, new GenericLinuxDeviceTester, parent);
- if (actionId == QLatin1String(Constants::GenericRemoteProcessesActionId)) {
- return new RemoteLinuxProcessesDialog(new GenericRemoteLinuxProcessList(deviceConfig),
- parent);
- }
+ return new LinuxDeviceTestDialog(lDevice, new GenericLinuxDeviceTester, parent);
+ if (actionId == QLatin1String(Constants::GenericRemoteProcessesActionId))
+ return new RemoteLinuxProcessesDialog(new GenericRemoteLinuxProcessList(lDevice, parent));
if (actionId == QLatin1String(Constants::GenericDeployKeyToDeviceActionId))
- return PublicKeyDeploymentDialog::createDialog(deviceConfig, parent);
+ return PublicKeyDeploymentDialog::createDialog(lDevice, parent);
return 0; // Can't happen.
}
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.h
index 24b4400b2f..193821763e 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationfactory.h
@@ -32,13 +32,14 @@
#ifndef GENERICLINUXDEVICECONFIGURATIONFACTORY_H
#define GENERICLINUXDEVICECONFIGURATIONFACTORY_H
-#include "linuxdeviceconfiguration.h"
#include "remotelinux_export.h"
+#include <projectexplorer/devicesupport/idevicefactory.h>
+
namespace RemoteLinux {
class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationFactory
- : public ILinuxDeviceConfigurationFactory
+ : public ProjectExplorer::IDeviceFactory
{
Q_OBJECT
@@ -46,16 +47,16 @@ public:
GenericLinuxDeviceConfigurationFactory(QObject *parent = 0);
QString displayName() const;
- ILinuxDeviceConfigurationWizard *createWizard(QWidget *parent) const;
- ILinuxDeviceConfigurationWidget *createWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent = 0) const;
- bool supportsOsType(const QString &osType) const;
- QString displayNameForOsType(const QString &osType) const;
+ ProjectExplorer::IDeviceWizard *createWizard(QWidget *parent) const;
+ ProjectExplorer::IDeviceWidget *createWidget(const ProjectExplorer::IDevice::Ptr &device,
+ QWidget *parent = 0) const;
+ ProjectExplorer::IDevice::Ptr loadDevice(const QVariantMap &map) const;
+ bool supportsDeviceType(const QString &deviceType) const;
+ QString displayNameForDeviceType(const QString &deviceType) const;
QStringList supportedDeviceActionIds() const;
QString displayNameForActionId(const QString &actionId) const;
QDialog *createDeviceAction(const QString &actionId,
- const LinuxDeviceConfiguration::ConstPtr &deviceConfig, QWidget *parent) const;
+ const ProjectExplorer::IDevice::ConstPtr &device, QWidget *parent) const;
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
index bbb32d03ea..e07ac7c535 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
@@ -32,19 +32,19 @@
#include "genericlinuxdeviceconfigurationwidget.h"
#include "ui_genericlinuxdeviceconfigurationwidget.h"
-#include <remotelinux/portlist.h>
-#include <remotelinux/linuxdeviceconfigurations.h>
-
+#include <utils/portlist.h>
#include <utils/ssh/sshconnection.h>
+#include <utils/ssh/sshkeycreationdialog.h>
#include <QTextStream>
using namespace RemoteLinux;
+using namespace Utils;
GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
const LinuxDeviceConfiguration::Ptr &deviceConfig,
QWidget *parent) :
- ILinuxDeviceConfigurationWidget(deviceConfig, parent),
+ ProjectExplorer::IDeviceWidget(deviceConfig, parent),
m_ui(new Ui::GenericLinuxDeviceConfigurationWidget)
{
m_ui->setupUi(this);
@@ -61,7 +61,7 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
connect(m_ui->sshPortSpinBox, SIGNAL(valueChanged(int)), this, SLOT(sshPortEditingFinished()));
connect(m_ui->showPasswordCheckBox, SIGNAL(toggled(bool)), this, SLOT(showPassword(bool)));
connect(m_ui->portsLineEdit, SIGNAL(editingFinished()), this, SLOT(handleFreePortsChanged()));
- connect(m_ui->makeKeyFileDefaultButton, SIGNAL(clicked()), SLOT(setDefaultKeyFilePath()));
+ connect(m_ui->createKeyButton, SIGNAL(clicked()), SLOT(createNewKey()));
initGui();
}
@@ -73,57 +73,56 @@ GenericLinuxDeviceConfigurationWidget::~GenericLinuxDeviceConfigurationWidget()
void GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
const bool usePassword = m_ui->passwordButton->isChecked();
sshParams.authenticationType = usePassword
- ? Utils::SshConnectionParameters::AuthenticationByPassword
- : Utils::SshConnectionParameters::AuthenticationByKey;
+ ? SshConnectionParameters::AuthenticationByPassword
+ : SshConnectionParameters::AuthenticationByKey;
deviceConfiguration()->setSshParameters(sshParams);
m_ui->pwdLineEdit->setEnabled(usePassword);
m_ui->passwordLabel->setEnabled(usePassword);
m_ui->keyFileLineEdit->setEnabled(!usePassword);
m_ui->keyLabel->setEnabled(!usePassword);
- m_ui->makeKeyFileDefaultButton->setEnabled(!usePassword);
}
void GenericLinuxDeviceConfigurationWidget::hostNameEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.host = m_ui->hostLineEdit->text();
deviceConfiguration()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.port = m_ui->sshPortSpinBox->value();
deviceConfiguration()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::timeoutEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.timeout = m_ui->timeoutSpinBox->value();
deviceConfiguration()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::userNameEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.userName = m_ui->userLineEdit->text();
deviceConfiguration()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::passwordEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.password = m_ui->pwdLineEdit->text();
deviceConfiguration()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished()
{
- Utils::SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
+ SshConnectionParameters sshParams = deviceConfiguration()->sshParameters();
sshParams.privateKeyFile = m_ui->keyFileLineEdit->path();
deviceConfiguration()->setSshParameters(sshParams);
}
@@ -140,17 +139,19 @@ void GenericLinuxDeviceConfigurationWidget::showPassword(bool showClearText)
? QLineEdit::Normal : QLineEdit::Password);
}
-void GenericLinuxDeviceConfigurationWidget::setDefaultKeyFilePath()
-{
- emit defaultSshKeyFilePathChanged(m_ui->keyFileLineEdit->path());
-}
-
void GenericLinuxDeviceConfigurationWidget::setPrivateKey(const QString &path)
{
m_ui->keyFileLineEdit->setPath(path);
keyFileEditingFinished();
}
+void GenericLinuxDeviceConfigurationWidget::createNewKey()
+{
+ SshKeyCreationDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted)
+ setPrivateKey(dialog.privateKeyFilePath());
+}
+
void GenericLinuxDeviceConfigurationWidget::updatePortsWarningLabel()
{
m_ui->portsWarningLabel->setVisible(!deviceConfiguration()->freePorts().hasMore());
@@ -158,18 +159,22 @@ void GenericLinuxDeviceConfigurationWidget::updatePortsWarningLabel()
void GenericLinuxDeviceConfigurationWidget::initGui()
{
+ if (deviceConfiguration()->machineType() == LinuxDeviceConfiguration::Hardware)
+ m_ui->machineTypeValueLabel->setText(tr("Physical Device"));
+ else
+ m_ui->machineTypeValueLabel->setText(tr("Emulator"));
m_ui->portsWarningLabel->setPixmap(QPixmap(":/projectexplorer/images/compile_error.png"));
m_ui->portsWarningLabel->setToolTip(QLatin1String("<font color=\"red\">")
+ tr("You will need at least one port.") + QLatin1String("</font>"));
- m_ui->keyFileLineEdit->setExpectedKind(Utils::PathChooser::File);
+ m_ui->keyFileLineEdit->setExpectedKind(PathChooser::File);
m_ui->keyFileLineEdit->lineEdit()->setMinimumWidth(0);
QRegExpValidator * const portsValidator
= new QRegExpValidator(QRegExp(PortList::regularExpression()), this);
m_ui->portsLineEdit->setValidator(portsValidator);
- const Utils::SshConnectionParameters &sshParams = deviceConfiguration()->sshParameters();
+ const SshConnectionParameters &sshParams = deviceConfiguration()->sshParameters();
- if (sshParams.authenticationType == Utils::SshConnectionParameters::AuthenticationByPassword)
+ if (sshParams.authenticationType == SshConnectionParameters::AuthenticationByPassword)
m_ui->passwordButton->setChecked(true);
else
m_ui->keyButton->setChecked(true);
@@ -187,3 +192,8 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
m_ui->showPasswordCheckBox->setChecked(false);
updatePortsWarningLabel();
}
+
+LinuxDeviceConfiguration::Ptr GenericLinuxDeviceConfigurationWidget::deviceConfiguration() const
+{
+ return device().staticCast<LinuxDeviceConfiguration>();
+}
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
index 7e5a3ef30d..14e53d5ff0 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
@@ -32,9 +32,10 @@
#ifndef REMOTELINUX_GENERICLINUXDEVICECONFIGURATIONWIDGET_H
#define REMOTELINUX_GENERICLINUXDEVICECONFIGURATIONWIDGET_H
-#include "remotelinux_export.h"
+#include <projectexplorer/devicesupport/idevicewidget.h>
#include "linuxdeviceconfiguration.h"
+#include "remotelinux_export.h"
namespace RemoteLinux {
@@ -42,14 +43,14 @@ namespace Ui {
class GenericLinuxDeviceConfigurationWidget;
}
-class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWidget : public ILinuxDeviceConfigurationWidget
+class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWidget
+ : public ProjectExplorer::IDeviceWidget
{
Q_OBJECT
public:
explicit GenericLinuxDeviceConfigurationWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent = 0);
+ const LinuxDeviceConfiguration::Ptr &deviceConfig, QWidget *parent = 0);
~GenericLinuxDeviceConfigurationWidget();
private slots:
@@ -62,12 +63,13 @@ private slots:
void keyFileEditingFinished();
void showPassword(bool showClearText);
void handleFreePortsChanged();
- void setDefaultKeyFilePath();
void setPrivateKey(const QString &path);
+ void createNewKey();
private:
void updatePortsWarningLabel();
void initGui();
+ LinuxDeviceConfiguration::Ptr deviceConfiguration() const;
Ui::GenericLinuxDeviceConfigurationWidget *m_ui;
};
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
index 6a72bec054..a1b2dbea5c 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>393</width>
- <height>170</height>
+ <height>206</height>
</rect>
</property>
<property name="windowTitle">
@@ -20,14 +20,14 @@
<property name="margin">
<number>0</number>
</property>
- <item row="0" column="0">
+ <item row="1" column="0">
<widget class="QLabel" name="authTypeLabel">
<property name="text">
<string>Authentication type:</string>
</property>
</widget>
</item>
- <item row="0" column="1">
+ <item row="1" column="1">
<widget class="QWidget" name="authTypeButtonsWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="margin">
@@ -63,7 +63,7 @@
</layout>
</widget>
</item>
- <item row="1" column="0">
+ <item row="2" column="0">
<widget class="QLabel" name="hostNameLabel">
<property name="text">
<string>&amp;Host name:</string>
@@ -73,7 +73,7 @@
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="hostLineEdit">
@@ -107,7 +107,7 @@
</item>
</layout>
</item>
- <item row="2" column="0">
+ <item row="3" column="0">
<widget class="QLabel" name="freePortsLabel">
<property name="text">
<string>Free ports:</string>
@@ -117,7 +117,7 @@
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="portsLineEdit">
@@ -161,7 +161,7 @@
</item>
</layout>
</item>
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QLabel" name="userNameLabel">
<property name="text">
<string>&amp;Username:</string>
@@ -171,10 +171,10 @@
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="4" column="1">
<widget class="QLineEdit" name="userLineEdit"/>
</item>
- <item row="4" column="0">
+ <item row="5" column="0">
<widget class="QLabel" name="passwordLabel">
<property name="text">
<string>&amp;Password:</string>
@@ -184,7 +184,7 @@
</property>
</widget>
</item>
- <item row="4" column="1">
+ <item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="pwdLineEdit">
@@ -202,27 +202,37 @@
</item>
</layout>
</item>
- <item row="5" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="keyLabel">
<property name="text">
<string>Private key file:</string>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="Utils::PathChooser" name="keyFileLineEdit" native="true"/>
</item>
<item>
- <widget class="QPushButton" name="makeKeyFileDefaultButton">
+ <widget class="QPushButton" name="createKeyButton">
<property name="text">
- <string>Set as Default</string>
+ <string>Create New...</string>
</property>
</widget>
</item>
</layout>
</item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="machineTypeLabel">
+ <property name="text">
+ <string>Machine type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="machineTypeValueLabel"/>
+ </item>
</layout>
<zorder>passwordLabel</zorder>
<zorder>authTypeButtonsWidget</zorder>
@@ -232,6 +242,8 @@
<zorder>userLineEdit</zorder>
<zorder>authTypeLabel</zorder>
<zorder>keyLabel</zorder>
+ <zorder>machineTypeLabel</zorder>
+ <zorder>machineTypeValueLabel</zorder>
</widget>
<customwidgets>
<customwidget>
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
index 30d5a302e4..0a5e290bc0 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
@@ -31,11 +31,13 @@
#include "genericlinuxdeviceconfigurationwizard.h"
#include "genericlinuxdeviceconfigurationwizardpages.h"
+#include "linuxdeviceconfiguration.h"
#include "linuxdevicetestdialog.h"
#include "linuxdevicetester.h"
-#include "portlist.h"
#include "remotelinux_constants.h"
+#include <utils/portlist.h>
+using namespace ProjectExplorer;
using namespace Utils;
namespace RemoteLinux {
@@ -58,7 +60,7 @@ public:
} // namespace Internal
GenericLinuxDeviceConfigurationWizard::GenericLinuxDeviceConfigurationWizard(QWidget *parent)
- : ILinuxDeviceConfigurationWizard(parent),
+ : IDeviceWizard(parent),
d(new Internal::GenericLinuxDeviceConfigurationWizardPrivate(this))
{
setWindowTitle(tr("New Generic Linux Device Configuration Setup"));
@@ -72,9 +74,9 @@ GenericLinuxDeviceConfigurationWizard::~GenericLinuxDeviceConfigurationWizard()
delete d;
}
-LinuxDeviceConfiguration::Ptr GenericLinuxDeviceConfigurationWizard::deviceConfiguration()
+IDevice::Ptr GenericLinuxDeviceConfigurationWizard::device()
{
- Utils::SshConnectionParameters sshParams(SshConnectionParameters::NoProxy);
+ Utils::SshConnectionParameters sshParams;
sshParams.host = d->setupPage.hostName();
sshParams.userName = d->setupPage.userName();
sshParams.port = 22;
@@ -85,8 +87,9 @@ LinuxDeviceConfiguration::Ptr GenericLinuxDeviceConfigurationWizard::deviceConfi
else
sshParams.privateKeyFile = d->setupPage.privateKeyFilePath();
LinuxDeviceConfiguration::Ptr devConf = LinuxDeviceConfiguration::create(d->setupPage.configurationName(),
- QLatin1String(Constants::GenericLinuxOsType), LinuxDeviceConfiguration::Hardware,
- PortList::fromString(QLatin1String("10000-10100")), sshParams);
+ QLatin1String(Constants::GenericLinuxOsType), LinuxDeviceConfiguration::Hardware);
+ devConf->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100")));
+ devConf->setSshParameters(sshParams);
LinuxDeviceTestDialog dlg(devConf, new GenericLinuxDeviceTester(this), this);
dlg.exec();
return devConf;
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h
index 9d57cb8fa6..a599f5edb2 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h
@@ -31,7 +31,9 @@
#ifndef GENERICLINUXDEVICECONFIGURATIONWIZARD_H
#define GENERICLINUXDEVICECONFIGURATIONWIZARD_H
-#include "linuxdeviceconfiguration.h"
+#include "remotelinux_export.h"
+
+#include <projectexplorer/devicesupport/idevicewizard.h>
namespace RemoteLinux {
namespace Internal {
@@ -39,7 +41,7 @@ class GenericLinuxDeviceConfigurationWizardPrivate;
} // namespace Internal
class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizard
- : public ILinuxDeviceConfigurationWizard
+ : public ProjectExplorer::IDeviceWizard
{
Q_OBJECT
@@ -47,7 +49,7 @@ public:
GenericLinuxDeviceConfigurationWizard(QWidget *parent = 0);
~GenericLinuxDeviceConfigurationWizard();
- LinuxDeviceConfiguration::Ptr deviceConfiguration();
+ ProjectExplorer::IDevice::Ptr device();
private:
Internal::GenericLinuxDeviceConfigurationWizardPrivate * const d;
diff --git a/src/plugins/remotelinux/linuxdeviceconfiguration.cpp b/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
index c35ca9b219..7ffc5e492f 100644
--- a/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
+++ b/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
@@ -31,15 +31,14 @@
**************************************************************************/
#include "linuxdeviceconfiguration.h"
-#include "linuxdeviceconfigurations.h"
-#include "portlist.h"
#include "remotelinux_constants.h"
+#include <utils/portlist.h>
#include <utils/ssh/sshconnection.h>
#include <utils/qtcassert.h>
-#include <QSettings>
#include <QDesktopServices>
+#include <QStringList>
using namespace Utils;
typedef SshConnectionParameters::AuthenticationType AuthType;
@@ -47,10 +46,7 @@ typedef SshConnectionParameters::AuthenticationType AuthType;
namespace RemoteLinux {
namespace Internal {
namespace {
-const QLatin1String NameKey("Name");
-const QLatin1String OldOsVersionKey("OsVersion"); // Outdated, only use for upgrading.
-const QLatin1String OsTypeKey("OsType");
-const QLatin1String TypeKey("Type");
+const QLatin1String MachineTypeKey("Type");
const QLatin1String HostKey("Host");
const QLatin1String SshPortKey("SshPort");
const QLatin1String PortsSpecKey("FreePortsSpec");
@@ -59,32 +55,18 @@ const QLatin1String AuthKey("Authentication");
const QLatin1String KeyFileKey("KeyFile");
const QLatin1String PasswordKey("Password");
const QLatin1String TimeoutKey("Timeout");
-const QLatin1String IsDefaultKey("IsDefault");
-const QLatin1String InternalIdKey("InternalId");
-const QLatin1String AttributesKey("Attributes");
const AuthType DefaultAuthType(SshConnectionParameters::AuthenticationByKey);
const int DefaultTimeout(10);
-const LinuxDeviceConfiguration::DeviceType DefaultDeviceType(LinuxDeviceConfiguration::Hardware);
+const LinuxDeviceConfiguration::MachineType DefaultMachineType(LinuxDeviceConfiguration::Hardware);
} // anonymous namespace
class LinuxDeviceConfigurationPrivate
{
public:
- LinuxDeviceConfigurationPrivate(const SshConnectionParameters &sshParameters)
- : sshParameters(sshParameters)
- {
- }
-
SshConnectionParameters sshParameters;
- QString displayName;
- QString osType;
- LinuxDeviceConfiguration::DeviceType deviceType;
+ LinuxDeviceConfiguration::MachineType machineType;
PortList freePorts;
- bool isDefault;
- LinuxDeviceConfiguration::Origin origin;
- LinuxDeviceConfiguration::Id internalId;
- QVariantHash attributes;
};
} // namespace Internal
@@ -96,88 +78,30 @@ LinuxDeviceConfiguration::~LinuxDeviceConfiguration()
delete d;
}
-LinuxDeviceConfiguration::Ptr LinuxDeviceConfiguration::create(const QSettings &settings,
- Id &nextId)
-{
- return Ptr(new LinuxDeviceConfiguration(settings, nextId));
-}
-
-LinuxDeviceConfiguration::Ptr LinuxDeviceConfiguration::create(const ConstPtr &other)
-{
- return Ptr(new LinuxDeviceConfiguration(other));
-}
-
LinuxDeviceConfiguration::Ptr LinuxDeviceConfiguration::create(const QString &name,
- const QString &osType, DeviceType deviceType, const PortList &freePorts,
- const SshConnectionParameters &sshParams, const QVariantHash &attributes, Origin origin)
+ const QString &type, MachineType machineType, Origin origin, const QString &fingerprint)
{
- return Ptr(new LinuxDeviceConfiguration(name, osType, deviceType, freePorts, sshParams,
- attributes, origin));
+ return Ptr(new LinuxDeviceConfiguration(name, type, machineType, origin, fingerprint));
}
-LinuxDeviceConfiguration::LinuxDeviceConfiguration(const QString &name, const QString &osType,
- DeviceType deviceType, const PortList &freePorts, const SshConnectionParameters &sshParams,
- const QVariantHash &attributes, Origin origin)
- : d(new LinuxDeviceConfigurationPrivate(sshParams))
+LinuxDeviceConfiguration::LinuxDeviceConfiguration() : d(new LinuxDeviceConfigurationPrivate)
{
- d->displayName = name;
- d->osType = osType;
- d->deviceType = deviceType;
- d->freePorts = freePorts;
- d->isDefault = false;
- d->origin = origin;
- d->attributes = attributes;
}
-LinuxDeviceConfiguration::LinuxDeviceConfiguration(const QSettings &settings, Id &nextId)
- : d(new LinuxDeviceConfigurationPrivate(SshConnectionParameters::NoProxy))
+LinuxDeviceConfiguration::LinuxDeviceConfiguration(const QString &name, const QString &type,
+ MachineType machineType, Origin origin, const QString &fingerprint)
+ : IDevice(type, origin, fingerprint), d(new LinuxDeviceConfigurationPrivate)
{
- d->origin = ManuallyAdded;
- d->displayName = settings.value(NameKey).toString();
- d->osType = settings.value(OsTypeKey).toString();
- d->deviceType = static_cast<DeviceType>(settings.value(TypeKey, DefaultDeviceType).toInt());
- d->isDefault = settings.value(IsDefaultKey, false).toBool();
- d->internalId = settings.value(InternalIdKey, nextId).toULongLong();
-
- if (d->internalId == nextId)
- ++nextId;
-
- d->attributes = settings.value(AttributesKey).toHash();
-
- // Convert from version < 2.3.
- if (d->osType.isEmpty()) {
- const int oldOsType = settings.value(OldOsVersionKey, -1).toInt();
- switch (oldOsType) {
- case 0: d->osType = QLatin1String("Maemo5OsType"); break;
- case 1: d->osType = QLatin1String("HarmattanOsType"); break;
- case 2: d->osType = QLatin1String("MeeGoOsType"); break;
- default: d->osType = QLatin1String(Constants::GenericLinuxOsType);
- }
- }
-
- d->freePorts = PortList::fromString(settings.value(PortsSpecKey, QLatin1String("10000-10100")).toString());
- d->sshParameters.host = settings.value(HostKey).toString();
- d->sshParameters.port = settings.value(SshPortKey, 22).toInt();
- d->sshParameters.userName = settings.value(UserNameKey).toString();
- d->sshParameters.authenticationType
- = static_cast<AuthType>(settings.value(AuthKey, DefaultAuthType).toInt());
- d->sshParameters.password = settings.value(PasswordKey).toString();
- d->sshParameters.privateKeyFile
- = settings.value(KeyFileKey, defaultPrivateKeyFilePath()).toString();
- d->sshParameters.timeout = settings.value(TimeoutKey, DefaultTimeout).toInt();
+ setDisplayName(name);
+ d->machineType = machineType;
}
-LinuxDeviceConfiguration::LinuxDeviceConfiguration(const LinuxDeviceConfiguration::ConstPtr &other)
- : d(new LinuxDeviceConfigurationPrivate(other->d->sshParameters))
+LinuxDeviceConfiguration::LinuxDeviceConfiguration(const LinuxDeviceConfiguration &other)
+ : IDevice(other), d(new LinuxDeviceConfigurationPrivate)
{
- d->displayName = other->d->displayName;
- d->osType = other->d->osType;
- d->deviceType = other->deviceType();
- d->freePorts = other->freePorts();
- d->isDefault = other->d->isDefault;
- d->origin = other->d->origin;
- d->internalId = other->d->internalId;
- d->attributes = other->d->attributes;
+ d->machineType = other.machineType();
+ d->freePorts = other.freePorts();
+ d->sshParameters = other.d->sshParameters;
}
QString LinuxDeviceConfiguration::defaultPrivateKeyFilePath()
@@ -191,94 +115,68 @@ QString LinuxDeviceConfiguration::defaultPublicKeyFilePath()
return defaultPrivateKeyFilePath() + QLatin1String(".pub");
}
-void LinuxDeviceConfiguration::save(QSettings &settings) const
+LinuxDeviceConfiguration::Ptr LinuxDeviceConfiguration::create()
{
- settings.setValue(NameKey, d->displayName);
- settings.setValue(OsTypeKey, d->osType);
- settings.setValue(TypeKey, d->deviceType);
- settings.setValue(HostKey, d->sshParameters.host);
- settings.setValue(SshPortKey, d->sshParameters.port);
- settings.setValue(PortsSpecKey, d->freePorts.toString());
- settings.setValue(UserNameKey, d->sshParameters.userName);
- settings.setValue(AuthKey, d->sshParameters.authenticationType);
- settings.setValue(PasswordKey, d->sshParameters.password);
- settings.setValue(KeyFileKey, d->sshParameters.privateKeyFile);
- settings.setValue(TimeoutKey, d->sshParameters.timeout);
- settings.setValue(IsDefaultKey, d->isDefault);
- settings.setValue(InternalIdKey, d->internalId);
- settings.setValue(AttributesKey, d->attributes);
+ return Ptr(new LinuxDeviceConfiguration);
}
-SshConnectionParameters LinuxDeviceConfiguration::sshParameters() const
+void LinuxDeviceConfiguration::fromMap(const QVariantMap &map)
{
- return d->sshParameters;
+ IDevice::fromMap(map);
+ d->machineType = static_cast<MachineType>(map.value(MachineTypeKey, DefaultMachineType).toInt());
+ d->freePorts = PortList::fromString(map.value(PortsSpecKey,
+ QLatin1String("10000-10100")).toString());
+ d->sshParameters.host = map.value(HostKey).toString();
+ d->sshParameters.port = map.value(SshPortKey, 22).toInt();
+ d->sshParameters.userName = map.value(UserNameKey).toString();
+ d->sshParameters.authenticationType
+ = static_cast<AuthType>(map.value(AuthKey, DefaultAuthType).toInt());
+ d->sshParameters.password = map.value(PasswordKey).toString();
+ d->sshParameters.privateKeyFile = map.value(KeyFileKey, defaultPrivateKeyFilePath()).toString();
+ d->sshParameters.timeout = map.value(TimeoutKey, DefaultTimeout).toInt();
}
-LinuxDeviceConfiguration::DeviceType LinuxDeviceConfiguration::deviceType() const
-
+QVariantMap LinuxDeviceConfiguration::toMap() const
{
- return d->deviceType;
+ QVariantMap map = IDevice::toMap();
+ map.insert(MachineTypeKey, d->machineType);
+ map.insert(HostKey, d->sshParameters.host);
+ map.insert(SshPortKey, d->sshParameters.port);
+ map.insert(PortsSpecKey, d->freePorts.toString());
+ map.insert(UserNameKey, d->sshParameters.userName);
+ map.insert(AuthKey, d->sshParameters.authenticationType);
+ map.insert(PasswordKey, d->sshParameters.password);
+ map.insert(KeyFileKey, d->sshParameters.privateKeyFile);
+ map.insert(TimeoutKey, d->sshParameters.timeout);
+ return map;
}
-LinuxDeviceConfiguration::Id LinuxDeviceConfiguration::internalId() const
+ProjectExplorer::IDevice::Ptr LinuxDeviceConfiguration::clone() const
{
- return d->internalId;
+ return Ptr(new LinuxDeviceConfiguration(*this));
}
-void LinuxDeviceConfiguration::setSshParameters(const SshConnectionParameters &sshParameters)
-{
- d->sshParameters = sshParameters;
-}
-
-void LinuxDeviceConfiguration::setFreePorts(const PortList &freePorts)
+SshConnectionParameters LinuxDeviceConfiguration::sshParameters() const
{
- d->freePorts = freePorts;
+ return d->sshParameters;
}
-void LinuxDeviceConfiguration::setAttribute(const QString &name, const QVariant &value)
-{
- d->attributes[name] = value;
-}
+LinuxDeviceConfiguration::MachineType LinuxDeviceConfiguration::machineType() const
-bool LinuxDeviceConfiguration::isAutoDetected() const
{
- return d->origin == AutoDetected;
+ return d->machineType;
}
-QVariantHash LinuxDeviceConfiguration::attributes() const
+void LinuxDeviceConfiguration::setSshParameters(const SshConnectionParameters &sshParameters)
{
- return d->attributes;
+ d->sshParameters = sshParameters;
}
-QVariant LinuxDeviceConfiguration::attribute(const QString &name) const
+void LinuxDeviceConfiguration::setFreePorts(const PortList &freePorts)
{
- return d->attributes.value(name);
+ d->freePorts = freePorts;
}
PortList LinuxDeviceConfiguration::freePorts() const { return d->freePorts; }
-QString LinuxDeviceConfiguration::displayName() const { return d->displayName; }
-QString LinuxDeviceConfiguration::osType() const { return d->osType; }
-bool LinuxDeviceConfiguration::isDefault() const { return d->isDefault; }
-
-void LinuxDeviceConfiguration::setDisplayName(const QString &name) { d->displayName = name; }
-void LinuxDeviceConfiguration::setInternalId(Id id) { d->internalId = id; }
-void LinuxDeviceConfiguration::setDefault(bool isDefault) { d->isDefault = isDefault; }
-
-const LinuxDeviceConfiguration::Id LinuxDeviceConfiguration::InvalidId = 0;
-
-
-ILinuxDeviceConfigurationWidget::ILinuxDeviceConfigurationWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent)
- : QWidget(parent),
- m_deviceConfiguration(deviceConfig)
-{
- QTC_CHECK(m_deviceConfiguration);
-}
-
-LinuxDeviceConfiguration::Ptr ILinuxDeviceConfigurationWidget::deviceConfiguration() const
-{
- return m_deviceConfiguration;
-}
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/linuxdeviceconfiguration.h b/src/plugins/remotelinux/linuxdeviceconfiguration.h
index d3504c4782..c03b29bef1 100644
--- a/src/plugins/remotelinux/linuxdeviceconfiguration.h
+++ b/src/plugins/remotelinux/linuxdeviceconfiguration.h
@@ -34,210 +34,57 @@
#include "remotelinux_export.h"
-#include <QSharedPointer>
-#include <QString>
-#include <QStringList>
-#include <QVariantHash>
-#include <QWizard>
-
-QT_BEGIN_NAMESPACE
-class QDialog;
-class QSettings;
-QT_END_NAMESPACE
+#include <projectexplorer/devicesupport/idevice.h>
namespace Utils {
class SshConnectionParameters;
+class PortList;
}
namespace RemoteLinux {
-class LinuxDeviceConfigurations;
-class PortList;
-
namespace Internal {
class LinuxDeviceConfigurationPrivate;
} // namespace Internal
-class REMOTELINUX_EXPORT LinuxDeviceConfiguration
+class REMOTELINUX_EXPORT LinuxDeviceConfiguration : public ProjectExplorer::IDevice
{
- friend class LinuxDeviceConfigurations;
public:
typedef QSharedPointer<LinuxDeviceConfiguration> Ptr;
typedef QSharedPointer<const LinuxDeviceConfiguration> ConstPtr;
- typedef quint64 Id;
-
- enum DeviceType { Hardware, Emulator };
- enum Origin { ManuallyAdded, AutoDetected };
+ enum MachineType { Hardware, Emulator };
~LinuxDeviceConfiguration();
- PortList freePorts() const;
+ Utils::PortList freePorts() const;
Utils::SshConnectionParameters sshParameters() const;
- QString displayName() const;
- QString osType() const;
- DeviceType deviceType() const;
- Id internalId() const;
- bool isDefault() const;
- bool isAutoDetected() const;
- QVariantHash attributes() const;
- QVariant attribute(const QString &name) const;
+ MachineType machineType() const;
void setSshParameters(const Utils::SshConnectionParameters &sshParameters);
- void setFreePorts(const PortList &freePorts);
- void setAttribute(const QString &name, const QVariant &value);
+ void setFreePorts(const Utils::PortList &freePorts);
static QString defaultPrivateKeyFilePath();
static QString defaultPublicKeyFilePath();
- static const Id InvalidId;
+ static Ptr create();
+ static Ptr create(const QString &name, const QString &type, MachineType machineType,
+ Origin origin = ManuallyAdded, const QString &fingerprint = QString());
- static Ptr create(const QString &name, const QString &osType, DeviceType deviceType,
- const PortList &freePorts, const Utils::SshConnectionParameters &sshParams,
- const QVariantHash &attributes = QVariantHash(), Origin origin = ManuallyAdded);
-private:
- LinuxDeviceConfiguration(const QString &name, const QString &osType, DeviceType deviceType,
- const PortList &freePorts, const Utils::SshConnectionParameters &sshParams,
- const QVariantHash &attributes, Origin origin);
- LinuxDeviceConfiguration(const QSettings &settings, Id &nextId);
- LinuxDeviceConfiguration(const ConstPtr &other);
-
- LinuxDeviceConfiguration(const LinuxDeviceConfiguration &);
- LinuxDeviceConfiguration &operator=(const LinuxDeviceConfiguration &);
-
- static Ptr create(const QSettings &settings, Id &nextId);
- static Ptr create(const ConstPtr &other);
-
- void setDisplayName(const QString &name);
- void setInternalId(Id id);
- void setDefault(bool isDefault);
- void save(QSettings &settings) const;
-
- Internal::LinuxDeviceConfigurationPrivate *d;
-};
-
-
-/*!
- \class RemoteLinux::ILinuxDeviceConfigurationWizard
-
- \brief Provides an interface for wizards creating a LinuxDeviceConfiguration
-
- A class implementing this interface is a wizard whose final result is
- a LinuxDeviceConfiguration object. The wizard will be started when the user chooses the
- "Add..." action from the "Linux devices" options page.
-*/
-class REMOTELINUX_EXPORT ILinuxDeviceConfigurationWizard : public QWizard
-{
- Q_OBJECT
-
-public:
- virtual LinuxDeviceConfiguration::Ptr deviceConfiguration() = 0;
+ void fromMap(const QVariantMap &map);
+ ProjectExplorer::IDevice::Ptr clone() const;
protected:
- ILinuxDeviceConfigurationWizard(QWidget *parent) : QWizard(parent) {}
-};
+ LinuxDeviceConfiguration();
+ LinuxDeviceConfiguration(const QString &name, const QString &type, MachineType machineType,
+ Origin origin, const QString &fingerprint);
+ LinuxDeviceConfiguration(const LinuxDeviceConfiguration &other);
-
-/*!
- \class RemoteLinux::LinuxDeviceConfigurationWidget : public QWidget
-
- \brief Provides an interface for the widget configuring a LinuxDeviceConfiguration
-
- A class implementing this interface will display a widget in the configuration
- options page "Linux Device", in the "Device configuration" tab.
- It's used to configure a particular device, the default widget is empty.
-*/
-class REMOTELINUX_EXPORT ILinuxDeviceConfigurationWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- ILinuxDeviceConfigurationWidget(const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent = 0);
-
-signals:
- void defaultSshKeyFilePathChanged(const QString &path);
-
-protected:
- LinuxDeviceConfiguration::Ptr deviceConfiguration() const;
+ QVariantMap toMap() const;
private:
- LinuxDeviceConfiguration::Ptr m_deviceConfiguration;
-};
-
-
-/*!
- \class RemoteLinux::ILinuxDeviceConfiguration factory.
-
- \brief Provides an interface for classes providing services related to certain type of Linux devices.
-
- The main service is a wizard providing the device configuration itself.
-
- The factory objects have to be added to the global object pool via
- \c ExtensionSystem::PluginManager::addObject().
- \sa ExtensionSystem::PluginManager::addObject()
-*/
-class REMOTELINUX_EXPORT ILinuxDeviceConfigurationFactory : public QObject
-{
- Q_OBJECT
-
-public:
- /*!
- A short, one-line description of what kind of device this factory supports.
- */
- virtual QString displayName() const = 0;
-
- /*!
- A wizard that can create the types of device configuration this factory supports.
- */
- virtual ILinuxDeviceConfigurationWizard *createWizard(QWidget *parent = 0) const = 0;
-
- /*!
- A widget that can configure the device this factory supports.
- */
- virtual ILinuxDeviceConfigurationWidget *createWidget(
- const LinuxDeviceConfiguration::Ptr &deviceConfig,
- QWidget *parent = 0) const = 0;
-
- /*!
- Returns true iff this factory supports the given device type.
- */
- virtual bool supportsOsType(const QString &osType) const = 0;
-
- /*!
- Returns a human-readable string for the given OS type, if this factory supports that type.
- */
- virtual QString displayNameForOsType(const QString &osType) const = 0;
-
- /*!
- Returns a list of ids representing actions that can be run on device configurations
- that this factory supports. These actions will be available in the "Linux Devices"
- options page.
- */
- virtual QStringList supportedDeviceActionIds() const = 0;
-
- /*!
- A human-readable string for the given id. Will be displayed on a button which, when clicked,
- will start the respective action.
- */
- virtual QString displayNameForActionId(const QString &actionId) const = 0;
-
- /*!
- True iff the user should be allowed to edit the device configurations created by this
- factory. Returns true by default. Override if your factory creates fixed configurations
- for which later editing makes no sense.
- */
- bool isUserEditable() const { return true; }
-
- /*!
- Produces a dialog implementing the respective action. The dialog is supposed to be
- modal, so implementers must make sure to make it interruptible as to not needlessly
- block the UI.
- */
- virtual QDialog *createDeviceAction(const QString &actionId,
- const LinuxDeviceConfiguration::ConstPtr &deviceConfig, QWidget *parent = 0) const = 0;
+ LinuxDeviceConfiguration &operator=(const LinuxDeviceConfiguration &);
-protected:
- ILinuxDeviceConfigurationFactory(QObject *parent) : QObject(parent) {}
+ Internal::LinuxDeviceConfigurationPrivate *d;
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/linuxdeviceconfigurations.cpp b/src/plugins/remotelinux/linuxdeviceconfigurations.cpp
deleted file mode 100644
index d1387ee6cf..0000000000
--- a/src/plugins/remotelinux/linuxdeviceconfigurations.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-#include "linuxdeviceconfigurations.h"
-
-#include "remotelinuxutils.h"
-
-#include <coreplugin/icore.h>
-#include <utils/qtcassert.h>
-
-#include <QHash>
-#include <QList>
-#include <QSettings>
-#include <QString>
-
-#include <algorithm>
-
-namespace RemoteLinux {
-namespace Internal {
-
-namespace {
-const QLatin1String SettingsGroup("MaemoDeviceConfigs");
-const QLatin1String IdCounterKey("IdCounter");
-const QLatin1String ConfigListKey("ConfigList");
-const QLatin1String DefaultKeyFilePathKey("DefaultKeyFile");
-
-bool cloningBlocked = false;
-
-class DevConfNameMatcher
-{
-public:
- DevConfNameMatcher(const QString &name) : m_name(name) {}
- bool operator()(const LinuxDeviceConfiguration::ConstPtr &devConfig)
- {
- return devConfig->displayName() == m_name;
- }
-private:
- const QString m_name;
-};
-
-} // anonymous namespace
-
-class LinuxDeviceConfigurationsPrivate
-{
-public:
- static LinuxDeviceConfigurations *instance;
- LinuxDeviceConfiguration::Id nextId;
- QList<LinuxDeviceConfiguration::Ptr> devConfigs;
- QString defaultSshKeyFilePath;
-};
-LinuxDeviceConfigurations *LinuxDeviceConfigurationsPrivate::instance = 0;
-
-} // namespace Internal
-
-using namespace Internal;
-
-
-LinuxDeviceConfigurations *LinuxDeviceConfigurations::instance(QObject *parent)
-{
- if (LinuxDeviceConfigurationsPrivate::instance == 0) {
- LinuxDeviceConfigurationsPrivate::instance = new LinuxDeviceConfigurations(parent);
- LinuxDeviceConfigurationsPrivate::instance->load();
- }
- return LinuxDeviceConfigurationsPrivate::instance;
-}
-
-void LinuxDeviceConfigurations::replaceInstance(const LinuxDeviceConfigurations *other)
-{
- Q_ASSERT(LinuxDeviceConfigurationsPrivate::instance);
-
- LinuxDeviceConfigurationsPrivate::instance->beginResetModel();
- copy(other, LinuxDeviceConfigurationsPrivate::instance, false);
- LinuxDeviceConfigurationsPrivate::instance->save();
- LinuxDeviceConfigurationsPrivate::instance->endResetModel();
- emit LinuxDeviceConfigurationsPrivate::instance->updated();
-}
-
-LinuxDeviceConfigurations *LinuxDeviceConfigurations::cloneInstance()
-{
- if (cloningBlocked)
- return 0;
- LinuxDeviceConfigurations * const other = new LinuxDeviceConfigurations(0);
- copy(LinuxDeviceConfigurationsPrivate::instance, other, true);
- return other;
-}
-
-void LinuxDeviceConfigurations::copy(const LinuxDeviceConfigurations *source,
- LinuxDeviceConfigurations *target, bool deep)
-{
- if (deep) {
- foreach (const LinuxDeviceConfiguration::ConstPtr &devConf, source->d->devConfigs)
- target->d->devConfigs << LinuxDeviceConfiguration::create(devConf);
- } else {
- target->d->devConfigs = source->d->devConfigs;
- }
- target->d->defaultSshKeyFilePath = source->d->defaultSshKeyFilePath;
- target->d->nextId = source->d->nextId;
-}
-
-void LinuxDeviceConfigurations::save()
-{
- QSettings *settings = Core::ICore::settings();
- settings->beginGroup(SettingsGroup);
- settings->setValue(IdCounterKey, d->nextId);
- settings->setValue(DefaultKeyFilePathKey, d->defaultSshKeyFilePath);
- settings->beginWriteArray(ConfigListKey);
- int skippedCount = 0;
- for (int i = 0; i < d->devConfigs.count(); ++i) {
- const LinuxDeviceConfiguration::ConstPtr &devConf = d->devConfigs.at(i);
- if (devConf->isAutoDetected()) {
- ++skippedCount;
- } else {
- settings->setArrayIndex(i-skippedCount);
- devConf->save(*settings);
- }
- }
- settings->endArray();
- settings->endGroup();
-}
-
-void LinuxDeviceConfigurations::addConfiguration(const LinuxDeviceConfiguration::Ptr &devConfig)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
-
- // Ensure uniqueness of name.
- QString name = devConfig->displayName();
- if (hasConfig(name)) {
- const QString nameTemplate = name + QLatin1String(" (%1)");
- int suffix = 2;
- do
- name = nameTemplate.arg(QString::number(suffix++));
- while (hasConfig(name));
- }
- devConfig->setDisplayName(name);
-
- devConfig->setInternalId(d->nextId++);
- beginInsertRows(QModelIndex(), rowCount(), rowCount());
- if (!defaultDeviceConfig(devConfig->osType()))
- devConfig->setDefault(true);
- d->devConfigs << devConfig;
- endInsertRows();
-}
-
-void LinuxDeviceConfigurations::removeConfiguration(int idx)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(idx >= 0 && idx < rowCount());
-
- beginRemoveRows(QModelIndex(), idx, idx);
- const bool wasDefault = deviceAt(idx)->isDefault();
- const QString osType = deviceAt(idx)->osType();
- d->devConfigs.removeAt(idx);
- endRemoveRows();
- if (wasDefault) {
- for (int i = 0; i < d->devConfigs.count(); ++i) {
- if (deviceAt(i)->osType() == osType) {
- d->devConfigs.at(i)->setDefault(true);
- const QModelIndex changedIndex = index(i, 0);
- emit dataChanged(changedIndex, changedIndex);
- break;
- }
- }
- }
-}
-
-void LinuxDeviceConfigurations::setDefaultSshKeyFilePath(const QString &path)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
-
- d->defaultSshKeyFilePath = path;
-}
-
-QString LinuxDeviceConfigurations::defaultSshKeyFilePath() const
-{
- return d->defaultSshKeyFilePath;
-}
-
-void LinuxDeviceConfigurations::setConfigurationName(int i, const QString &name)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(i >= 0 && i < rowCount());
-
- d->devConfigs.at(i)->setDisplayName(name);
- const QModelIndex changedIndex = index(i, 0);
- emit dataChanged(changedIndex, changedIndex);
-}
-
-void LinuxDeviceConfigurations::setSshParameters(int i,
- const Utils::SshConnectionParameters &params)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(i >= 0 && i < rowCount());
-
- d->devConfigs.at(i)->setSshParameters(params);
-}
-
-void LinuxDeviceConfigurations::setFreePorts(int i, const PortList &freePorts)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(i >= 0 && i < rowCount());
-
- d->devConfigs.at(i)->setFreePorts(freePorts);
-}
-
-void LinuxDeviceConfigurations::setDefaultDevice(int idx)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(idx >= 0 && idx < rowCount());
-
- const LinuxDeviceConfiguration::Ptr &devConf = d->devConfigs.at(idx);
- if (devConf->isDefault())
- return;
- QModelIndex oldDefaultIndex;
- for (int i = 0; i < d->devConfigs.count(); ++i) {
- const LinuxDeviceConfiguration::Ptr &oldDefaultDev = d->devConfigs.at(i);
- if (oldDefaultDev->isDefault() && oldDefaultDev->osType() == devConf->osType()) {
- oldDefaultDev->setDefault(false);
- oldDefaultIndex = index(i, 0);
- break;
- }
- }
-
- QTC_CHECK(oldDefaultIndex.isValid());
- emit dataChanged(oldDefaultIndex, oldDefaultIndex);
- devConf->setDefault(true);
- const QModelIndex newDefaultIndex = index(idx, 0);
- emit dataChanged(newDefaultIndex, newDefaultIndex);
-}
-
-void LinuxDeviceConfigurations::setAttribute(int index, const QString &name, const QVariant &value)
-{
- QTC_ASSERT(this != LinuxDeviceConfigurationsPrivate::instance, return);
- Q_ASSERT(index >= 0 && index < rowCount());
-
- d->devConfigs.at(index)->setAttribute(name, value);
-}
-
-LinuxDeviceConfigurations::LinuxDeviceConfigurations(QObject *parent)
- : QAbstractListModel(parent), d(new LinuxDeviceConfigurationsPrivate)
-{
-}
-
-LinuxDeviceConfiguration::Ptr LinuxDeviceConfigurations::mutableDeviceAt(int idx) const
-{
- Q_ASSERT(idx >= 0 && idx < rowCount());
- return d->devConfigs.at(idx);
-}
-
-void LinuxDeviceConfigurations::blockCloning()
-{
- QTC_ASSERT(!cloningBlocked, return);
- cloningBlocked = true;
-}
-
-void LinuxDeviceConfigurations::unblockCloning()
-{
- QTC_ASSERT(cloningBlocked, return);
- cloningBlocked = false;
- emit instance()->cloningPossible();
-}
-
-LinuxDeviceConfigurations::~LinuxDeviceConfigurations()
-{
- delete d;
-}
-
-void LinuxDeviceConfigurations::load()
-{
- QSettings *settings = Core::ICore::settings();
- settings->beginGroup(SettingsGroup);
- d->nextId = settings->value(IdCounterKey, 1).toULongLong();
- d->defaultSshKeyFilePath = settings->value(DefaultKeyFilePathKey,
- LinuxDeviceConfiguration::defaultPrivateKeyFilePath()).toString();
- int count = settings->beginReadArray(ConfigListKey);
- for (int i = 0; i < count; ++i) {
- settings->setArrayIndex(i);
- LinuxDeviceConfiguration::Ptr devConf
- = LinuxDeviceConfiguration::create(*settings, d->nextId);
- d->devConfigs << devConf;
- }
- settings->endArray();
- settings->endGroup();
- ensureOneDefaultConfigurationPerOsType();
-}
-
-LinuxDeviceConfiguration::ConstPtr LinuxDeviceConfigurations::deviceAt(int idx) const
-{
- Q_ASSERT(idx >= 0 && idx < rowCount());
- return d->devConfigs.at(idx);
-}
-
-bool LinuxDeviceConfigurations::hasConfig(const QString &name) const
-{
- QList<LinuxDeviceConfiguration::Ptr>::ConstIterator resultIt =
- std::find_if(d->devConfigs.constBegin(), d->devConfigs.constEnd(),
- DevConfNameMatcher(name));
- return resultIt != d->devConfigs.constEnd();
-}
-
-LinuxDeviceConfiguration::ConstPtr LinuxDeviceConfigurations::find(LinuxDeviceConfiguration::Id id) const
-{
- const int index = indexForInternalId(id);
- return index == -1 ? LinuxDeviceConfiguration::ConstPtr() : deviceAt(index);
-}
-
-LinuxDeviceConfiguration::ConstPtr LinuxDeviceConfigurations::defaultDeviceConfig(const QString &osType) const
-{
- foreach (const LinuxDeviceConfiguration::ConstPtr &devConf, d->devConfigs) {
- if (devConf->isDefault() && devConf->osType() == osType)
- return devConf;
- }
- return LinuxDeviceConfiguration::ConstPtr();
-}
-
-int LinuxDeviceConfigurations::indexForInternalId(LinuxDeviceConfiguration::Id internalId) const
-{
- for (int i = 0; i < d->devConfigs.count(); ++i) {
- if (deviceAt(i)->internalId() == internalId)
- return i;
- }
- return -1;
-}
-
-LinuxDeviceConfiguration::Id LinuxDeviceConfigurations::internalId(LinuxDeviceConfiguration::ConstPtr devConf) const
-{
- return devConf ? devConf->internalId() : LinuxDeviceConfiguration::InvalidId;
-}
-
-void LinuxDeviceConfigurations::ensureOneDefaultConfigurationPerOsType()
-{
- QHash<QString, bool> osTypeHasDefault;
-
- // Step 1: Ensure there's at most one default configuration per device type.
- foreach (const LinuxDeviceConfiguration::Ptr &devConf, d->devConfigs) {
- if (devConf->isDefault()) {
- if (osTypeHasDefault.value(devConf->osType()))
- devConf->setDefault(false);
- else
- osTypeHasDefault.insert(devConf->osType(), true);
- }
- }
-
- // Step 2: Ensure there's at least one default configuration per device type.
- foreach (const LinuxDeviceConfiguration::Ptr &devConf, d->devConfigs) {
- if (!osTypeHasDefault.value(devConf->osType())) {
- devConf->setDefault(true);
- osTypeHasDefault.insert(devConf->osType(), true);
- }
- }
-}
-
-int LinuxDeviceConfigurations::rowCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent);
- return d->devConfigs.count();
-}
-
-QVariant LinuxDeviceConfigurations::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid() || index.row() >= rowCount() || role != Qt::DisplayRole)
- return QVariant();
- const LinuxDeviceConfiguration::ConstPtr devConf = deviceAt(index.row());
- QString name = devConf->displayName();
- if (devConf->isDefault()) {
- name += QLatin1Char(' ') + tr("(default for %1)")
- .arg(RemoteLinuxUtils::osTypeToString(devConf->osType()));
- }
- return name;
-}
-
-} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/linuxdeviceconfigurations.h b/src/plugins/remotelinux/linuxdeviceconfigurations.h
deleted file mode 100644
index b79be8a165..0000000000
--- a/src/plugins/remotelinux/linuxdeviceconfigurations.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-#ifndef LINUXDEVICECONFIGURATIONS_H
-#define LINUXDEVICECONFIGURATIONS_H
-
-#include "linuxdeviceconfiguration.h"
-#include "remotelinux_export.h"
-
-#include <QAbstractListModel>
-
-QT_FORWARD_DECLARE_CLASS(QString)
-
-namespace RemoteLinux {
-namespace Internal {
-class LinuxDeviceConfigurationsPrivate;
-class LinuxDeviceConfigurationsSettingsWidget;
-} // namespace Internal
-
-class REMOTELINUX_EXPORT LinuxDeviceConfigurations : public QAbstractListModel
-{
- Q_OBJECT
- friend class Internal::LinuxDeviceConfigurationsSettingsWidget;
-public:
- ~LinuxDeviceConfigurations();
-
- static LinuxDeviceConfigurations *instance(QObject *parent = 0);
-
- // If you want to edit the list of device configurations programatically
- // (e.g. when doing device auto-detection), call cloneInstance() to get a copy,
- // do the changes there and then call replaceInstance() to write them back.
- // Callers must be prepared to deal with cloneInstance() temporarily returning null,
- // which happens if the settings dialog is currently open.
- // All other callers are required to do the clone/replace operation synchronously!
- static void replaceInstance(const LinuxDeviceConfigurations *other);
- static LinuxDeviceConfigurations *cloneInstance();
-
- LinuxDeviceConfiguration::ConstPtr deviceAt(int index) const;
- LinuxDeviceConfiguration::ConstPtr find(LinuxDeviceConfiguration::Id id) const;
- LinuxDeviceConfiguration::ConstPtr defaultDeviceConfig(const QString &osType) const;
- bool hasConfig(const QString &name) const;
- int indexForInternalId(LinuxDeviceConfiguration::Id internalId) const;
- LinuxDeviceConfiguration::Id internalId(LinuxDeviceConfiguration::ConstPtr devConf) const;
-
- QString defaultSshKeyFilePath() const;
-
- void addConfiguration(const LinuxDeviceConfiguration::Ptr &devConfig);
- void removeConfiguration(int index);
- void setConfigurationName(int i, const QString &name);
- void setSshParameters(int i, const Utils::SshConnectionParameters &params);
- void setFreePorts(int i, const PortList &freePorts);
- void setDefaultDevice(int index);
- void setAttribute(int index, const QString &name, const QVariant &value);
-
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
- virtual QVariant data(const QModelIndex &index,
- int role = Qt::DisplayRole) const;
-
-public slots:
- void setDefaultSshKeyFilePath(const QString &path);
-
-signals:
- void updated();
- void cloningPossible();
-
-private:
- LinuxDeviceConfigurations(QObject *parent);
-
- LinuxDeviceConfiguration::Ptr mutableDeviceAt(int index) const;
-
- static void blockCloning();
- static void unblockCloning();
-
- void load();
- void save();
- static void copy(const LinuxDeviceConfigurations *source,
- LinuxDeviceConfigurations *target, bool deep);
- void ensureOneDefaultConfigurationPerOsType();
-
- Internal::LinuxDeviceConfigurationsPrivate * const d;
-};
-
-} // namespace RemoteLinux
-
-#endif // LINUXDEVICECONFIGURATIONS_H
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index c1b915a58e..e7b32afa51 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -159,7 +159,7 @@ void GenericLinuxDeviceTester::handleProcessFinished(int exitStatus)
emit progressMessage(tr("Checking if specified ports are available..."));
d->state = TestingPorts;
- d->portsGatherer.start(d->connection, d->deviceConfiguration);
+ d->portsGatherer.start(d->deviceConfiguration);
}
void GenericLinuxDeviceTester::handlePortsGatheringError(const QString &message)
diff --git a/src/plugins/remotelinux/remotelinux.pro b/src/plugins/remotelinux/remotelinux.pro
index 186f71f1f8..25ccbf6dd8 100644
--- a/src/plugins/remotelinux/remotelinux.pro
+++ b/src/plugins/remotelinux/remotelinux.pro
@@ -13,9 +13,7 @@ HEADERS += \
remotelinuxplugin.h \
remotelinux_export.h \
linuxdeviceconfiguration.h \
- linuxdeviceconfigurations.h \
remotelinuxrunconfiguration.h \
- linuxdevicefactoryselectiondialog.h \
publickeydeploymentdialog.h \
genericlinuxdeviceconfigurationwizard.h \
genericlinuxdeviceconfigurationfactory.h \
@@ -26,7 +24,6 @@ HEADERS += \
remotelinuxruncontrolfactory.h \
remotelinuxdebugsupport.h \
genericlinuxdeviceconfigurationwizardpages.h \
- portlist.h \
deployablefile.h \
deployablefilesperprofile.h \
deploymentinfo.h \
@@ -48,13 +45,10 @@ HEADERS += \
linuxdevicetestdialog.h \
remotelinuxprocesslist.h \
remotelinuxprocessesdialog.h \
- linuxdeviceconfigurationssettingswidget.h \
remotelinuxenvironmentreader.h \
sshkeydeployer.h \
typespecificdeviceconfigurationlistmodel.h \
- sshkeycreationdialog.h \
remotelinuxusedportsgatherer.h \
- remotelinuxsettingspages.h \
remotelinuxutils.h \
deploymentsettingsassistant.h \
remotelinuxdeployconfigurationwidget.h \
@@ -72,9 +66,7 @@ SOURCES += \
embeddedlinuxqtversionfactory.cpp \
remotelinuxplugin.cpp \
linuxdeviceconfiguration.cpp \
- linuxdeviceconfigurations.cpp \
remotelinuxrunconfiguration.cpp \
- linuxdevicefactoryselectiondialog.cpp \
publickeydeploymentdialog.cpp \
genericlinuxdeviceconfigurationwizard.cpp \
genericlinuxdeviceconfigurationfactory.cpp \
@@ -85,7 +77,6 @@ SOURCES += \
remotelinuxruncontrolfactory.cpp \
remotelinuxdebugsupport.cpp \
genericlinuxdeviceconfigurationwizardpages.cpp \
- portlist.cpp \
deployablefilesperprofile.cpp \
deploymentinfo.cpp \
abstractremotelinuxdeploystep.cpp \
@@ -105,13 +96,10 @@ SOURCES += \
linuxdevicetestdialog.cpp \
remotelinuxprocesslist.cpp \
remotelinuxprocessesdialog.cpp \
- linuxdeviceconfigurationssettingswidget.cpp \
remotelinuxenvironmentreader.cpp \
sshkeydeployer.cpp \
typespecificdeviceconfigurationlistmodel.cpp \
- sshkeycreationdialog.cpp \
remotelinuxusedportsgatherer.cpp \
- remotelinuxsettingspages.cpp \
remotelinuxutils.cpp \
deploymentsettingsassistant.cpp \
remotelinuxdeployconfigurationwidget.cpp \
@@ -123,12 +111,9 @@ SOURCES += \
genericlinuxdeviceconfigurationwidget.cpp
FORMS += \
- linuxdevicefactoryselectiondialog.ui \
genericlinuxdeviceconfigurationwizardsetuppage.ui \
linuxdevicetestdialog.ui \
remotelinuxprocessesdialog.ui \
- linuxdeviceconfigurationssettingswidget.ui \
- sshkeycreationdialog.ui \
remotelinuxdeployconfigurationwidget.ui \
profilesupdatedialog.ui \
genericlinuxdeviceconfigurationwidget.ui
diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs
index 4ebd6d01cf..fe4d63af7c 100644
--- a/src/plugins/remotelinux/remotelinux.qbs
+++ b/src/plugins/remotelinux/remotelinux.qbs
@@ -60,17 +60,10 @@ QtcPlugin {
"genericremotelinuxdeploystepfactory.h",
"linuxdeviceconfiguration.cpp",
"linuxdeviceconfiguration.h",
- "linuxdeviceconfigurations.cpp",
- "linuxdeviceconfigurations.h",
- "linuxdevicefactoryselectiondialog.cpp",
- "linuxdevicefactoryselectiondialog.h",
- "linuxdevicefactoryselectiondialog.ui",
"linuxdevicetestdialog.cpp",
"linuxdevicetestdialog.h",
"linuxdevicetester.cpp",
"linuxdevicetester.h",
- "portlist.cpp",
- "portlist.h",
"publickeydeploymentdialog.cpp",
"publickeydeploymentdialog.h",
"remotelinux.qrc",
@@ -102,21 +95,14 @@ QtcPlugin {
"remotelinuxrunconfigurationwidget.cpp",
"remotelinuxrunconfigurationwidget.h",
"remotelinuxruncontrol.h",
- "remotelinuxsettingspages.cpp",
- "remotelinuxsettingspages.h",
"remotelinuxusedportsgatherer.cpp",
"remotelinuxutils.cpp",
"remotelinuxutils.h",
- "sshkeycreationdialog.cpp",
- "sshkeycreationdialog.h",
"startgdbserverdialog.cpp",
"startgdbserverdialog.h",
"tarpackagecreationstep.h",
"uploadandinstalltarpackagestep.h",
"genericdirectuploadservice.h",
- "linuxdeviceconfigurationssettingswidget.cpp",
- "linuxdeviceconfigurationssettingswidget.h",
- "linuxdeviceconfigurationssettingswidget.ui",
"linuxdevicetestdialog.ui",
"packageuploader.cpp",
"packageuploader.h",
@@ -130,7 +116,6 @@ QtcPlugin {
"remotelinuxprocessesdialog.h",
"remotelinuxprocessesdialog.ui",
"remotelinuxusedportsgatherer.h",
- "sshkeycreationdialog.ui",
"sshkeydeployer.cpp",
"sshkeydeployer.h",
"typespecificdeviceconfigurationlistmodel.cpp",
diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h
index fba006162f..dc7c90df81 100644
--- a/src/plugins/remotelinux/remotelinux_constants.h
+++ b/src/plugins/remotelinux/remotelinux_constants.h
@@ -37,9 +37,6 @@ namespace Constants {
const char GenericLinuxOsType[] = "GenericLinuxOsType";
-const char RemoteLinuxSettingsCategory[] = "X.RemoteLinux";
-const char RemoteLinuxSettingsPageId[] = "AA.Remote Linux Device Configurations";
-
const char GenericTestDeviceActionId[] = "RemoteLinux.GenericTestDeviceAction";
const char GenericDeployKeyToDeviceActionId[] = "RemoteLinux.GenericDeployKeyToDeviceAction";
const char GenericRemoteProcessesActionId[] = "RemoteLinux.GenericRemoteProcessesAction";
diff --git a/src/plugins/remotelinux/remotelinuxapplicationrunner.cpp b/src/plugins/remotelinux/remotelinuxapplicationrunner.cpp
index 6c00de18ee..1485db51c5 100644
--- a/src/plugins/remotelinux/remotelinuxapplicationrunner.cpp
+++ b/src/plugins/remotelinux/remotelinuxapplicationrunner.cpp
@@ -32,10 +32,10 @@
#include "remotelinuxapplicationrunner.h"
#include "linuxdeviceconfiguration.h"
-#include "portlist.h"
#include "remotelinuxrunconfiguration.h"
#include "remotelinuxusedportsgatherer.h"
+#include <utils/portlist.h>
#include <utils/qtcassert.h>
#include <utils/ssh/sshconnection.h>
#include <utils/ssh/sshconnectionmanager.h>
@@ -422,7 +422,7 @@ void AbstractRemoteLinuxApplicationRunner::handleInitialCleanupDone(bool success
}
d->state = GatheringPorts;
- d->portsGatherer.start(d->connection, d->devConfig);
+ d->portsGatherer.start(d->devConfig);
}
void AbstractRemoteLinuxApplicationRunner::handleInitializationsDone(bool success)
diff --git a/src/plugins/remotelinux/remotelinuxapplicationrunner.h b/src/plugins/remotelinux/remotelinuxapplicationrunner.h
index 86cd9cb66a..92f2b053c0 100644
--- a/src/plugins/remotelinux/remotelinuxapplicationrunner.h
+++ b/src/plugins/remotelinux/remotelinuxapplicationrunner.h
@@ -37,12 +37,12 @@
#include <QSharedPointer>
namespace Utils {
+class PortList;
class SshConnection;
}
namespace RemoteLinux {
class LinuxDeviceConfiguration;
-class PortList;
class RemoteLinuxRunConfiguration;
class RemoteLinuxUsedPortsGatherer;
@@ -67,7 +67,7 @@ public:
QSharedPointer<Utils::SshConnection> connection() const;
QSharedPointer<const LinuxDeviceConfiguration> devConfig() const;
const RemoteLinuxUsedPortsGatherer *usedPortsGatherer() const;
- PortList *freePorts();
+ Utils::PortList *freePorts();
QString remoteExecutable() const;
QString arguments() const;
QString commandPrefix() const;
diff --git a/src/plugins/remotelinux/remotelinuxdebugsupport.cpp b/src/plugins/remotelinux/remotelinuxdebugsupport.cpp
index 20c12f91af..73344d11bc 100644
--- a/src/plugins/remotelinux/remotelinuxdebugsupport.cpp
+++ b/src/plugins/remotelinux/remotelinuxdebugsupport.cpp
@@ -110,10 +110,7 @@ DebuggerStartParameters AbstractRemoteLinuxDebugSupport::startParameters(const R
params.startMode = AttachToRemoteServer;
params.executable = runConfig->localExecutableFilePath();
params.debuggerCommand = runConfig->gdbCmd();
- QString host = devConf->sshParameters().host;
- params.remoteChannel = host.contains(QLatin1Char(':'))
- ? QString::fromLatin1("[%1]:-1").arg(host)
- : host + QLatin1String(":-1");
+ params.remoteChannel = devConf->sshParameters().host + QLatin1String(":-1");
// TODO: This functionality should be inside the debugger.
const ProjectExplorer::Abi &abi = runConfig->target()
@@ -210,11 +207,14 @@ void AbstractRemoteLinuxDebugSupport::startExecution()
.arg(d->qmlPort);
}
+ const QHostAddress peerAddress = runner()->connection()->connectionInfo().peerAddress;
+ QString peerAddressString = peerAddress.toString();
+ if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol)
+ peerAddressString.prepend(QLatin1Char('[')).append(QLatin1Char(']'));
const QString remoteCommandLine = (d->qmlDebugging && !d->cppDebugging)
? QString::fromLatin1("%1 %2 %3").arg(runner()->commandPrefix()).arg(remoteExe).arg(args)
: QString::fromLatin1("%1 gdbserver %5:%2 %3 %4").arg(runner()->commandPrefix())
- .arg(d->gdbServerPort).arg(remoteExe).arg(args)
- .arg(runner()->connection()->connectionInfo().peerAddress.toString());
+ .arg(d->gdbServerPort).arg(remoteExe).arg(args).arg(peerAddressString);
connect(runner(), SIGNAL(remoteProcessFinished(qint64)),
SLOT(handleRemoteProcessFinished(qint64)));
runner()->startExecution(remoteCommandLine.toUtf8());
diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
index 0b1a0333bb..7c77a918f6 100644
--- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
@@ -33,10 +33,10 @@
#include "abstractembeddedlinuxtarget.h"
#include "deploymentinfo.h"
-#include "linuxdeviceconfigurations.h"
#include "remotelinuxdeployconfigurationwidget.h"
#include "typespecificdeviceconfigurationlistmodel.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <qt4projectmanager/qt4target.h>
using namespace ProjectExplorer;
@@ -82,13 +82,13 @@ RemoteLinuxDeployConfiguration::~RemoteLinuxDeployConfiguration()
void RemoteLinuxDeployConfiguration::initialize()
{
d->deviceConfiguration = target()->deviceConfigModel()->defaultDeviceConfig();
- connect(target()->deviceConfigModel(), SIGNAL(updated()),
+ connect(target()->deviceConfigModel(), SIGNAL(modelReset()),
SLOT(handleDeviceConfigurationListUpdated()));
}
void RemoteLinuxDeployConfiguration::handleDeviceConfigurationListUpdated()
{
- setDeviceConfig(LinuxDeviceConfigurations::instance()->internalId(d->deviceConfiguration));
+ setDeviceConfig(DeviceManager::instance()->internalId(d->deviceConfiguration));
}
void RemoteLinuxDeployConfiguration::setDeviceConfig(LinuxDeviceConfiguration::Id internalId)
@@ -102,8 +102,7 @@ bool RemoteLinuxDeployConfiguration::fromMap(const QVariantMap &map)
{
if (!DeployConfiguration::fromMap(map))
return false;
- setDeviceConfig(map.value(QLatin1String(DeviceIdKey),
- LinuxDeviceConfiguration::InvalidId).toULongLong());
+ setDeviceConfig(map.value(QLatin1String(DeviceIdKey), IDevice::invalidId()).toULongLong());
return true;
}
@@ -111,7 +110,7 @@ QVariantMap RemoteLinuxDeployConfiguration::toMap() const
{
QVariantMap map = DeployConfiguration::toMap();
map.insert(QLatin1String(DeviceIdKey),
- LinuxDeviceConfigurations::instance()->internalId(d->deviceConfiguration));
+ DeviceManager::instance()->internalId(d->deviceConfiguration));
return map;
}
diff --git a/src/plugins/remotelinux/remotelinuxdeployconfigurationwidget.cpp b/src/plugins/remotelinux/remotelinuxdeployconfigurationwidget.cpp
index 30f71dfc52..950f7a68f2 100644
--- a/src/plugins/remotelinux/remotelinuxdeployconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/remotelinuxdeployconfigurationwidget.cpp
@@ -34,13 +34,13 @@
#include "abstractembeddedlinuxtarget.h"
#include "deployablefilesperprofile.h"
#include "deploymentinfo.h"
-#include "linuxdeviceconfigurations.h"
#include "remotelinuxdeployconfiguration.h"
-#include "remotelinuxsettingspages.h"
#include "typespecificdeviceconfigurationlistmodel.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <utils/qtcassert.h>
#include <QTreeView>
@@ -182,8 +182,7 @@ void RemoteLinuxDeployConfigurationWidget::handleDeviceConfigurationListChanged(
{
const LinuxDeviceConfiguration::ConstPtr &devConf
= d->deployConfiguration->deviceConfiguration();
- const LinuxDeviceConfiguration::Id internalId
- = LinuxDeviceConfigurations::instance()->internalId(devConf);
+ const IDevice::Id internalId = DeviceManager::instance()->internalId(devConf);
const int newIndex
= d->deployConfiguration->target()->deviceConfigModel()->indexForInternalId(internalId);
d->ui.deviceConfigsComboBox->setCurrentIndex(newIndex);
@@ -191,8 +190,9 @@ void RemoteLinuxDeployConfigurationWidget::handleDeviceConfigurationListChanged(
void RemoteLinuxDeployConfigurationWidget::showDeviceConfigurations()
{
- Core::ICore::showOptionsDialog(LinuxDeviceConfigurationsSettingsPage::pageCategory(),
- LinuxDeviceConfigurationsSettingsPage::pageId());
+ Core::ICore::showOptionsDialog(
+ QLatin1String(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY),
+ QLatin1String(ProjectExplorer::Constants::DEVICE_SETTINGS_PAGE_ID));
}
void RemoteLinuxDeployConfigurationWidget::openProjectFile()
diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp
index 0703560562..0cf91f4fdf 100644
--- a/src/plugins/remotelinux/remotelinuxplugin.cpp
+++ b/src/plugins/remotelinux/remotelinuxplugin.cpp
@@ -37,12 +37,10 @@
#include "deployablefile.h"
#include "genericlinuxdeviceconfigurationfactory.h"
#include "genericremotelinuxdeploystepfactory.h"
-#include "linuxdeviceconfigurations.h"
#include "qt4projectmanager/qt4projectmanagerconstants.h"
#include "remotelinuxdeployconfigurationfactory.h"
#include "remotelinuxrunconfigurationfactory.h"
#include "remotelinuxruncontrolfactory.h"
-#include "remotelinuxsettingspages.h"
#include "startgdbserverdialog.h"
#include <coreplugin/icore.h>
@@ -71,10 +69,7 @@ bool RemoteLinuxPlugin::initialize(const QStringList &arguments,
Q_UNUSED(arguments)
Q_UNUSED(errorMessage)
- LinuxDeviceConfigurations::instance(this);
-
addObject(this);
- addAutoReleasedObject(new LinuxDeviceConfigurationsSettingsPage);
addAutoReleasedObject(new GenericLinuxDeviceConfigurationFactory);
addAutoReleasedObject(new RemoteLinuxRunConfigurationFactory);
addAutoReleasedObject(new RemoteLinuxRunControlFactory);
diff --git a/src/plugins/remotelinux/remotelinuxplugin.h b/src/plugins/remotelinux/remotelinuxplugin.h
index b65e6da10d..7307bc8ea4 100644
--- a/src/plugins/remotelinux/remotelinuxplugin.h
+++ b/src/plugins/remotelinux/remotelinuxplugin.h
@@ -41,6 +41,7 @@ namespace Internal {
class RemoteLinuxPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "RemoteLinux.json")
public:
RemoteLinuxPlugin();
diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
index 6b20963ba5..67392b4cd6 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
@@ -34,7 +34,6 @@
#include "deploymentinfo.h"
#include "linuxdeviceconfiguration.h"
-#include "portlist.h"
#include "remotelinuxdeployconfiguration.h"
#include "remotelinuxrunconfigurationwidget.h"
@@ -47,10 +46,12 @@
#include <qt4projectmanager/qt4project.h>
#include <qt4projectmanager/qt4target.h>
+#include <utils/portlist.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
using namespace Qt4ProjectManager;
+using namespace Utils;
namespace RemoteLinux {
namespace Internal {
@@ -94,8 +95,8 @@ public:
QString gdbPath;
QString arguments;
RemoteLinuxRunConfiguration::BaseEnvironmentType baseEnvironmentType;
- Utils::Environment remoteEnvironment;
- QList<Utils::EnvironmentItem> userEnvironmentChanges;
+ Environment remoteEnvironment;
+ QList<EnvironmentItem> userEnvironmentChanges;
bool validParse;
bool parseInProgress;
QString disabledReason;
@@ -185,7 +186,7 @@ QWidget *RemoteLinuxRunConfiguration::createConfigurationWidget()
return new RemoteLinuxRunConfigurationWidget(this);
}
-Utils::OutputFormatter *RemoteLinuxRunConfiguration::createOutputFormatter() const
+OutputFormatter *RemoteLinuxRunConfiguration::createOutputFormatter() const
{
return new QtSupport::QtOutputFormatter(qt4Target()->qt4Project());
}
@@ -211,7 +212,7 @@ QVariantMap RemoteLinuxRunConfiguration::toMap() const
map.insert(QLatin1String(ProFileKey), dir.relativeFilePath(d->proFilePath));
map.insert(QLatin1String(BaseEnvironmentBaseKey), d->baseEnvironmentType);
map.insert(QLatin1String(UserEnvironmentChangesKey),
- Utils::EnvironmentItem::toStringList(d->userEnvironmentChanges));
+ EnvironmentItem::toStringList(d->userEnvironmentChanges));
map.insert(QLatin1String(UseAlternateExeKey), d->useAlternateRemoteExecutable);
map.insert(QLatin1String(AlternateExeKey), d->alternateRemoteExecutable);
map.insert(QLatin1String(WorkingDirectoryKey), d->workingDirectory);
@@ -227,7 +228,7 @@ bool RemoteLinuxRunConfiguration::fromMap(const QVariantMap &map)
const QDir dir = QDir(target()->project()->projectDirectory());
d->proFilePath = QDir::cleanPath(dir.filePath(map.value(QLatin1String(ProFileKey)).toString()));
d->userEnvironmentChanges =
- Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(UserEnvironmentChangesKey))
+ EnvironmentItem::fromStringList(map.value(QLatin1String(UserEnvironmentChangesKey))
.toStringList());
d->baseEnvironmentType = static_cast<BaseEnvironmentType>(map.value(QLatin1String(BaseEnvironmentBaseKey),
RemoteBaseEnvironment).toInt());
@@ -417,20 +418,20 @@ void RemoteLinuxRunConfiguration::setBaseEnvironmentType(BaseEnvironmentType env
}
}
-Utils::Environment RemoteLinuxRunConfiguration::environment() const
+Environment RemoteLinuxRunConfiguration::environment() const
{
- Utils::Environment env = baseEnvironment();
+ Environment env = baseEnvironment();
env.modify(userEnvironmentChanges());
return env;
}
-Utils::Environment RemoteLinuxRunConfiguration::baseEnvironment() const
+Environment RemoteLinuxRunConfiguration::baseEnvironment() const
{
return (d->baseEnvironmentType == RemoteBaseEnvironment ? remoteEnvironment()
- : Utils::Environment());
+ : Environment());
}
-QList<Utils::EnvironmentItem> RemoteLinuxRunConfiguration::userEnvironmentChanges() const
+QList<EnvironmentItem> RemoteLinuxRunConfiguration::userEnvironmentChanges() const
{
return d->userEnvironmentChanges;
}
@@ -439,13 +440,13 @@ QString RemoteLinuxRunConfiguration::userEnvironmentChangesAsString() const
{
QString env;
QString placeHolder = QLatin1String("%1=%2 ");
- foreach (const Utils::EnvironmentItem &item, userEnvironmentChanges())
+ foreach (const EnvironmentItem &item, userEnvironmentChanges())
env.append(placeHolder.arg(item.name, item.value));
return env.mid(0, env.size() - 1);
}
void RemoteLinuxRunConfiguration::setUserEnvironmentChanges(
- const QList<Utils::EnvironmentItem> &diff)
+ const QList<EnvironmentItem> &diff)
{
if (d->userEnvironmentChanges != diff) {
d->userEnvironmentChanges = diff;
@@ -453,12 +454,12 @@ void RemoteLinuxRunConfiguration::setUserEnvironmentChanges(
}
}
-Utils::Environment RemoteLinuxRunConfiguration::remoteEnvironment() const
+Environment RemoteLinuxRunConfiguration::remoteEnvironment() const
{
return d->remoteEnvironment;
}
-void RemoteLinuxRunConfiguration::setRemoteEnvironment(const Utils::Environment &environment)
+void RemoteLinuxRunConfiguration::setRemoteEnvironment(const Environment &environment)
{
if (d->remoteEnvironment.size() == 0 || d->remoteEnvironment != environment) {
d->remoteEnvironment = environment;
diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.h b/src/plugins/remotelinux/remotelinuxrunconfiguration.h
index cd702ebe17..29b4656877 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfiguration.h
+++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.h
@@ -44,9 +44,12 @@ class Qt4BaseTarget;
class Qt4ProFileNode;
} // namespace Qt4ProjectManager
+namespace Utils {
+class PortList;
+}
+
namespace RemoteLinux {
class LinuxDeviceConfiguration;
-class PortList;
class RemoteLinuxRunConfigurationWidget;
class RemoteLinuxDeployConfiguration;
@@ -85,7 +88,7 @@ public:
virtual QString environmentPreparationCommand() const;
virtual QString commandPrefix() const;
- virtual PortList freePorts() const;
+ virtual Utils::PortList freePorts() const;
QString localExecutableFilePath() const;
QString defaultRemoteExecutableFilePath() const;
@@ -131,7 +134,9 @@ protected:
QString defaultDisplayName();
void setDisabledReason(const QString &reason) const;
QString userEnvironmentChangesAsString() const;
- Q_SLOT void updateEnabledState() { emit isEnabledChanged(isEnabled()); }
+
+protected slots:
+ void updateEnabledState() { emit enabledChanged(); }
private slots:
void proFileUpdate(Qt4ProjectManager::Qt4ProFileNode *pro, bool success, bool parseInProgress);
diff --git a/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.cpp b/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.cpp
index 10bc98e890..bf30a70ff5 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.cpp
@@ -34,12 +34,12 @@
#include "linuxdeviceconfiguration.h"
#include "remotelinuxrunconfiguration.h"
#include "remotelinuxenvironmentreader.h"
-#include "remotelinuxsettingspages.h"
#include "remotelinuxutils.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <projectexplorer/environmentwidget.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
#include <qt4projectmanager/qt4target.h>
#include <utils/detailswidget.h>
@@ -114,9 +114,9 @@ RemoteLinuxRunConfigurationWidget::RemoteLinuxRunConfigurationWidget(RemoteLinux
connect(d->runConfiguration, SIGNAL(deviceConfigurationChanged(ProjectExplorer::Target*)),
SLOT(handleCurrentDeviceConfigChanged()));
handleCurrentDeviceConfigChanged();
- connect(d->runConfiguration, SIGNAL(isEnabledChanged(bool)),
- SLOT(runConfigurationEnabledChange(bool)));
- runConfigurationEnabledChange(d->runConfiguration->isEnabled());
+ connect(d->runConfiguration, SIGNAL(enabledChanged()),
+ SLOT(runConfigurationEnabledChange()));
+ runConfigurationEnabledChange();
}
RemoteLinuxRunConfigurationWidget::~RemoteLinuxRunConfigurationWidget()
@@ -141,8 +141,9 @@ void RemoteLinuxRunConfigurationWidget::addDisabledLabel(QVBoxLayout *topLayout)
topLayout->addLayout(hl);
}
-void RemoteLinuxRunConfigurationWidget::runConfigurationEnabledChange(bool enabled)
+void RemoteLinuxRunConfigurationWidget::runConfigurationEnabledChange()
{
+ bool enabled = d->runConfiguration->isEnabled();
d->topWidget.setEnabled(enabled);
d->disabledIcon.setVisible(!enabled);
d->disabledReason.setVisible(!enabled);
@@ -289,8 +290,9 @@ void RemoteLinuxRunConfigurationWidget::handleWorkingDirectoryChanged()
void RemoteLinuxRunConfigurationWidget::showDeviceConfigurationsDialog(const QString &link)
{
if (link == QLatin1String("deviceconfig")) {
- Core::ICore::showOptionsDialog(LinuxDeviceConfigurationsSettingsPage::pageCategory(),
- LinuxDeviceConfigurationsSettingsPage::pageId());
+ Core::ICore::showOptionsDialog(
+ QLatin1String(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY),
+ QLatin1String(ProjectExplorer::Constants::DEVICE_SETTINGS_PAGE_ID));
} else if (link == QLatin1String("debugger")) {
Core::ICore::showOptionsDialog(QLatin1String("O.Debugger"),
QLatin1String("M.Gdb"));
diff --git a/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.h b/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.h
index 74c5a90420..840c3b54f1 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.h
+++ b/src/plugins/remotelinux/remotelinuxrunconfigurationwidget.h
@@ -60,7 +60,7 @@ public:
void addFormLayoutRow(QWidget *label, QWidget *field);
void addDisabledLabel(QVBoxLayout *topLayout);
- Q_SLOT void runConfigurationEnabledChange(bool enabled);
+ Q_SLOT void runConfigurationEnabledChange();
private slots:
void argumentsEdited(const QString &args);
diff --git a/src/plugins/remotelinux/remotelinuxruncontrolfactory.cpp b/src/plugins/remotelinux/remotelinuxruncontrolfactory.cpp
index d283e10d1b..1e9ee52ae4 100644
--- a/src/plugins/remotelinux/remotelinuxruncontrolfactory.cpp
+++ b/src/plugins/remotelinux/remotelinuxruncontrolfactory.cpp
@@ -31,7 +31,6 @@
#include "remotelinuxruncontrolfactory.h"
-#include "portlist.h"
#include "remotelinuxdebugsupport.h"
#include "remotelinuxrunconfiguration.h"
#include "remotelinuxruncontrol.h"
@@ -41,6 +40,7 @@
#include <debugger/debuggerrunner.h>
#include <debugger/debuggerstartparameters.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <utils/portlist.h>
using namespace Debugger;
using namespace ProjectExplorer;
diff --git a/src/plugins/remotelinux/remotelinuxusedportsgatherer.cpp b/src/plugins/remotelinux/remotelinuxusedportsgatherer.cpp
index ade18dfcbb..23c442b52d 100644
--- a/src/plugins/remotelinux/remotelinuxusedportsgatherer.cpp
+++ b/src/plugins/remotelinux/remotelinuxusedportsgatherer.cpp
@@ -31,9 +31,12 @@
#include "remotelinuxusedportsgatherer.h"
#include "linuxdeviceconfiguration.h"
-#include "portlist.h"
-#include <utils/ssh/sshremoteprocessrunner.h>
+#include <utils/portlist.h>
+#include <utils/qtcassert.h>
+#include <utils/ssh/sshconnection.h>
+#include <utils/ssh/sshconnectionmanager.h>
+#include <utils/ssh/sshremoteprocess.h>
#include <QString>
@@ -45,14 +48,12 @@ namespace Internal {
class RemoteLinuxUsedPortsGathererPrivate
{
public:
- RemoteLinuxUsedPortsGathererPrivate() : running(false) {}
-
- SshRemoteProcessRunner procRunner;
+ SshConnection::Ptr connection;
+ SshRemoteProcess::Ptr process;
PortList portsToCheck;
QList<int> usedPorts;
QByteArray remoteStdout;
QByteArray remoteStderr;
- bool running; // TODO: Redundant due to being in sync with procRunner?
};
} // namespace Internal
@@ -69,24 +70,26 @@ RemoteLinuxUsedPortsGatherer::~RemoteLinuxUsedPortsGatherer()
delete d;
}
-void RemoteLinuxUsedPortsGatherer::start(const Utils::SshConnection::Ptr &connection,
- const LinuxDeviceConfiguration::ConstPtr &devConf)
+void RemoteLinuxUsedPortsGatherer::start(const LinuxDeviceConfiguration::ConstPtr &devConf)
{
- if (d->running)
- qWarning("Unexpected call of %s in running state", Q_FUNC_INFO);
+ QTC_ASSERT(!d->connection, return);
d->portsToCheck = devConf->freePorts();
- d->usedPorts.clear();
- d->remoteStdout.clear();
- d->remoteStderr.clear();
- connect(&d->procRunner, SIGNAL(connectionError()), SLOT(handleConnectionError()));
- connect(&d->procRunner, SIGNAL(processClosed(int)), SLOT(handleProcessClosed(int)));
- connect(&d->procRunner, SIGNAL(processOutputAvailable(QByteArray)),
- SLOT(handleRemoteStdOut(QByteArray)));
- connect(&d->procRunner, SIGNAL(processErrorOutputAvailable(QByteArray)),
- SLOT(handleRemoteStdErr(QByteArray)));
+ d->connection = SshConnectionManager::instance().acquireConnection(devConf->sshParameters());
+ connect(d->connection.data(), SIGNAL(error(Utils::SshError)), SLOT(handleConnectionError()));
+ if (d->connection->state() == SshConnection::Connected) {
+ handleConnectionEstablished();
+ return;
+ }
+ connect(d->connection.data(), SIGNAL(connected()), SLOT(handleConnectionEstablished()));
+ if (d->connection->state() == SshConnection::Unconnected)
+ d->connection->connectToHost();
+}
+
+void RemoteLinuxUsedPortsGatherer::handleConnectionEstablished()
+{
QString procFilePath;
int addressLength;
- if (connection->connectionInfo().localAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ if (d->connection->connectionInfo().localAddress.protocol() == QAbstractSocket::IPv4Protocol) {
procFilePath = QLatin1String("/proc/net/tcp");
addressLength = 8;
} else {
@@ -96,19 +99,28 @@ void RemoteLinuxUsedPortsGatherer::start(const Utils::SshConnection::Ptr &connec
const QString command = QString::fromLatin1("sed "
"'s/.*: [[:xdigit:]]\\{%1\\}:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' %2")
.arg(addressLength).arg(procFilePath);
+ d->process = d->connection->createRemoteProcess(command.toUtf8());
+
+ connect(d->process.data(), SIGNAL(closed(int)), SLOT(handleProcessClosed(int)));
+ connect(d->process.data(), SIGNAL(readyReadStandardOutput()), SLOT(handleRemoteStdOut()));
+ connect(d->process.data(), SIGNAL(readyReadStandardError()), SLOT(handleRemoteStdErr()));
- // TODO: We should not use an SshRemoteProcessRunner here, because we have to check
- // for the type of the connection before we can say what the exact command line is.
- d->procRunner.run(command.toUtf8(), connection->connectionParameters());
- d->running = true;
+ d->process->start();
}
void RemoteLinuxUsedPortsGatherer::stop()
{
- if (!d->running)
+ if (!d->connection)
return;
- d->running = false;
- disconnect(&d->procRunner, 0, this, 0);
+ d->usedPorts.clear();
+ d->remoteStdout.clear();
+ d->remoteStderr.clear();
+ if (d->process)
+ disconnect(d->process.data(), 0, this, 0);
+ d->process.clear();
+ disconnect(d->connection.data(), 0, this, 0);
+ SshConnectionManager::instance().releaseConnection(d->connection);
+ d->connection.clear();
}
int RemoteLinuxUsedPortsGatherer::getNextFreePort(PortList *freePorts) const
@@ -148,33 +160,29 @@ void RemoteLinuxUsedPortsGatherer::setupUsedPorts()
void RemoteLinuxUsedPortsGatherer::handleConnectionError()
{
- if (!d->running)
+ if (!d->connection)
return;
- emit error(tr("Connection error: %1").arg(d->procRunner.lastConnectionErrorString()));
+ emit error(tr("Connection error: %1").arg(d->connection->errorString()));
stop();
}
void RemoteLinuxUsedPortsGatherer::handleProcessClosed(int exitStatus)
{
- if (!d->running)
+ if (!d->connection)
return;
QString errMsg;
switch (exitStatus) {
case SshRemoteProcess::FailedToStart:
- errMsg = tr("Could not start remote process: %1")
- .arg(d->procRunner.processErrorString());
+ errMsg = tr("Could not start remote process: %1").arg(d->process->errorString());
break;
case SshRemoteProcess::KilledBySignal:
- errMsg = tr("Remote process crashed: %1")
- .arg(d->procRunner.processErrorString());
+ errMsg = tr("Remote process crashed: %1").arg(d->process->errorString());
break;
case SshRemoteProcess::ExitedNormally:
- if (d->procRunner.processExitCode() == 0) {
+ if (d->process->exitCode() == 0)
setupUsedPorts();
- } else {
- errMsg = tr("Remote process failed; exit code was %1.")
- .arg(d->procRunner.processExitCode());
- }
+ else
+ errMsg = tr("Remote process failed; exit code was %1.").arg(d->process->exitCode());
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid exit status");
@@ -190,14 +198,16 @@ void RemoteLinuxUsedPortsGatherer::handleProcessClosed(int exitStatus)
stop();
}
-void RemoteLinuxUsedPortsGatherer::handleRemoteStdOut(const QByteArray &output)
+void RemoteLinuxUsedPortsGatherer::handleRemoteStdOut()
{
- d->remoteStdout += output;
+ if (d->process)
+ d->remoteStdout += d->process->readAllStandardOutput();
}
-void RemoteLinuxUsedPortsGatherer::handleRemoteStdErr(const QByteArray &output)
+void RemoteLinuxUsedPortsGatherer::handleRemoteStdErr()
{
- d->remoteStderr += output;
+ if (d->process)
+ d->remoteStderr += d->process->readAllStandardError();
}
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/remotelinuxusedportsgatherer.h b/src/plugins/remotelinux/remotelinuxusedportsgatherer.h
index 16e40f9965..f3efb31ee1 100644
--- a/src/plugins/remotelinux/remotelinuxusedportsgatherer.h
+++ b/src/plugins/remotelinux/remotelinuxusedportsgatherer.h
@@ -39,13 +39,10 @@
QT_FORWARD_DECLARE_CLASS(QString)
-namespace Utils {
-class SshConnection;
-} // namespace Utils
+namespace Utils { class PortList; }
namespace RemoteLinux {
class LinuxDeviceConfiguration;
-class PortList;
namespace Internal {
class RemoteLinuxUsedPortsGathererPrivate;
@@ -58,10 +55,9 @@ class REMOTELINUX_EXPORT RemoteLinuxUsedPortsGatherer : public QObject
public:
explicit RemoteLinuxUsedPortsGatherer(QObject *parent = 0);
~RemoteLinuxUsedPortsGatherer();
- void start(const QSharedPointer<Utils::SshConnection> &connection,
- const QSharedPointer<const LinuxDeviceConfiguration> &devConf);
+ void start(const QSharedPointer<const LinuxDeviceConfiguration> &devConf);
void stop();
- int getNextFreePort(PortList *freePorts) const; // returns -1 if no more are left
+ int getNextFreePort(Utils::PortList *freePorts) const; // returns -1 if no more are left
QList<int> usedPorts() const;
signals:
@@ -69,10 +65,11 @@ signals:
void portListReady();
private slots:
+ void handleConnectionEstablished();
void handleConnectionError();
void handleProcessClosed(int exitStatus);
- void handleRemoteStdOut(const QByteArray &output);
- void handleRemoteStdErr(const QByteArray &output);
+ void handleRemoteStdOut();
+ void handleRemoteStdErr();
private:
void setupUsedPorts();
diff --git a/src/plugins/remotelinux/remotelinuxutils.cpp b/src/plugins/remotelinux/remotelinuxutils.cpp
index 327af7a2e4..c8aa2d6c25 100644
--- a/src/plugins/remotelinux/remotelinuxutils.cpp
+++ b/src/plugins/remotelinux/remotelinuxutils.cpp
@@ -34,60 +34,14 @@
#include "linuxdeviceconfiguration.h"
#include <extensionsystem/pluginmanager.h>
-#include <projectexplorer/abi.h>
-#include <qt4projectmanager/qt4buildconfiguration.h>
-#include <qt4projectmanager/qt4target.h>
-#include <qtsupport/baseqtversion.h>
#include <QCoreApplication>
#include <QList>
#include <QString>
using namespace ExtensionSystem;
-using namespace ProjectExplorer;
-using namespace Qt4ProjectManager;
-using namespace QtSupport;
namespace RemoteLinux {
-namespace Internal {
-namespace {
-
-bool isUnixQt(const BaseQtVersion *qtVersion)
-{
- if (!qtVersion)
- return false;
- const QList<Abi> &abis = qtVersion->qtAbis();
- foreach (const Abi &abi, abis) {
- switch (abi.os()) {
- case Abi::UnixOS: case Abi::BsdOS: case Abi::LinuxOS: case Abi::MacOS: return true;
- default: continue;
- }
- }
- return false;
-}
-
-} // anonymous namespace
-} // namespace Internal
-
-bool RemoteLinuxUtils::hasUnixQt(const Target *target)
-{
- const Qt4BaseTarget * const qtTarget = qobject_cast<const Qt4BaseTarget *>(target);
- if (!qtTarget)
- return false;
- const Qt4BuildConfiguration * const bc = qtTarget->activeQt4BuildConfiguration();
- return bc && Internal::isUnixQt(bc->qtVersion());
-}
-
-QString RemoteLinuxUtils::osTypeToString(const QString &osType)
-{
- const QList<ILinuxDeviceConfigurationFactory *> &factories
- = PluginManager::instance()->getObjects<ILinuxDeviceConfigurationFactory>();
- foreach (const ILinuxDeviceConfigurationFactory * const factory, factories) {
- if (factory->supportsOsType(osType))
- return factory->displayNameForOsType(osType);
- }
- return QCoreApplication::translate("RemoteLinux", "Unknown OS");
-}
QString RemoteLinuxUtils::deviceConfigurationName(const LinuxDeviceConfiguration::ConstPtr &devConf)
{
diff --git a/src/plugins/remotelinux/remotelinuxutils.h b/src/plugins/remotelinux/remotelinuxutils.h
index cec48c67c6..a365fbd616 100644
--- a/src/plugins/remotelinux/remotelinuxutils.h
+++ b/src/plugins/remotelinux/remotelinuxutils.h
@@ -36,18 +36,12 @@
#include <QSharedPointer>
-namespace ProjectExplorer {
-class Target;
-}
-
namespace RemoteLinux {
class LinuxDeviceConfiguration;
class REMOTELINUX_EXPORT RemoteLinuxUtils
{
public:
- static bool hasUnixQt(const ProjectExplorer::Target *target);
- static QString osTypeToString(const QString &osType);
static QString deviceConfigurationName(const QSharedPointer<const LinuxDeviceConfiguration> &devConf);
};
diff --git a/src/plugins/remotelinux/startgdbserverdialog.cpp b/src/plugins/remotelinux/startgdbserverdialog.cpp
index ca7526c0fb..f2475b4b5e 100644
--- a/src/plugins/remotelinux/startgdbserverdialog.cpp
+++ b/src/plugins/remotelinux/startgdbserverdialog.cpp
@@ -34,15 +34,14 @@
#include "remotelinuxprocesslist.h"
#include "linuxdeviceconfiguration.h"
-#include "linuxdeviceconfigurations.h"
#include "remotelinuxusedportsgatherer.h"
-#include "portlist.h"
#include <coreplugin/icore.h>
-
#include <extensionsystem/pluginmanager.h>
-
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/devicesupport/devicemanagermodel.h>
#include <utils/pathchooser.h>
+#include <utils/portlist.h>
#include <utils/qtcassert.h>
#include <utils/ssh/sshconnection.h>
#include <utils/ssh/sshremoteprocessrunner.h>
@@ -70,6 +69,7 @@
#include <QVBoxLayout>
using namespace Core;
+using namespace ProjectExplorer;
using namespace Utils;
const char LastSysroot[] = "RemoteLinux/LastSysroot";
@@ -87,8 +87,9 @@ public:
LinuxDeviceConfiguration::ConstPtr currentDevice() const
{
- LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance();
- return devices->deviceAt(deviceComboBox->currentIndex());
+ DeviceManager *devices = DeviceManager::instance();
+ return devices->deviceAt(deviceComboBox->currentIndex())
+ .dynamicCast<const LinuxDeviceConfiguration>();
}
StartGdbServerDialog *q;
@@ -174,15 +175,16 @@ StartGdbServerDialog::StartGdbServerDialog(QWidget *parent) :
{
setWindowTitle(tr("List of Remote Processes"));
- LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance();
+ DeviceManager *devices = DeviceManager::instance();
+ DeviceManagerModel * const model = new DeviceManagerModel(devices, this);
QObject::connect(d->closeButton, SIGNAL(clicked()), this, SLOT(reject()));
- d->deviceComboBox->setModel(devices);
+ d->deviceComboBox->setModel(model);
d->deviceComboBox->setCurrentIndex(d->settings->value(LastDevice).toInt());
connect(&d->gatherer, SIGNAL(error(QString)), SLOT(portGathererError(QString)));
connect(&d->gatherer, SIGNAL(portListReady()), SLOT(portListReady()));
- if (devices->rowCount() == 0) {
+ if (devices->deviceCount() == 0) {
d->tableView->setEnabled(false);
} else {
d->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
@@ -217,9 +219,13 @@ StartGdbServerDialog::~StartGdbServerDialog()
void StartGdbServerDialog::attachToDevice(int index)
{
- LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance();
+ DeviceManager *devices = DeviceManager::instance();
+ LinuxDeviceConfiguration::ConstPtr device
+ = devices->deviceAt(index).dynamicCast<const LinuxDeviceConfiguration>();
+ if (!device)
+ return;
delete d->processList;
- d->processList = new GenericRemoteLinuxProcessList(devices->deviceAt(index));
+ d->processList = new GenericRemoteLinuxProcessList(device);
d->proxyModel.setSourceModel(d->processList);
connect(d->processList, SIGNAL(error(QString)),
SLOT(handleRemoteError(QString)));
@@ -260,6 +266,8 @@ void StartGdbServerDialog::attachToProcess()
d->attachProcessButton->setEnabled(false);
LinuxDeviceConfiguration::ConstPtr device = d->currentDevice();
+ if (!device)
+ return;
PortList ports = device->freePorts();
const int port = d->gatherer.getNextFreePort(&ports);
const int row = d->proxyModel.mapToSource(indexes.first()).row();
@@ -317,8 +325,7 @@ void StartGdbServerDialog::startGdbServer()
d->startServerOnly = true;
if (exec() == QDialog::Rejected)
return;
- LinuxDeviceConfiguration::ConstPtr device = d->currentDevice();
- d->gatherer.start(SshConnection::create(device->sshParameters()), device);
+ d->gatherer.start(d->currentDevice());
}
void StartGdbServerDialog::attachToRemoteProcess()
@@ -326,8 +333,7 @@ void StartGdbServerDialog::attachToRemoteProcess()
d->startServerOnly = false;
if (exec() == QDialog::Rejected)
return;
- LinuxDeviceConfiguration::ConstPtr device = d->currentDevice();
- d->gatherer.start(SshConnection::create(device->sshParameters()), device);
+ d->gatherer.start(d->currentDevice());
}
void StartGdbServerDialog::handleConnectionError()
@@ -363,12 +369,7 @@ void StartGdbServerDialog::reportOpenPort(int port)
{
logMessage(tr("Port %1 is now accessible.").arg(port));
LinuxDeviceConfiguration::ConstPtr device = d->currentDevice();
- QString host = device->sshParameters().host;
- QString channel;
- if (host.contains(QLatin1Char(':')))
- channel = QString::fromLatin1("[%1]:%2").arg(host).arg(port);
- else
- channel = QString::fromLatin1("%1:%2").arg(host).arg(port);
+ QString channel = QString("%1:%2").arg(device->sshParameters().host).arg(port);
logMessage(tr("Server started on %1").arg(channel));
const char *member = d->startServerOnly ? "gdbServerStarted" : "attachedToProcess";
@@ -399,8 +400,8 @@ void StartGdbServerDialog::startGdbServerOnPort(int port, int pid)
SLOT(handleProcessErrorOutput(QByteArray)));
connect(&d->runner, SIGNAL(processClosed(int)), SLOT(handleProcessClosed(int)));
- QByteArray cmd = "/usr/bin/gdbserver --attach localhost:"
- + QByteArray::number(port) + ' ' + QByteArray::number(pid);
+ QByteArray cmd = "/usr/bin/gdbserver --attach :"
+ + QByteArray::number(port) + " " + QByteArray::number(pid);
logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd)));
d->runner.run(cmd, device->sshParameters());
}
diff --git a/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.cpp b/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.cpp
index 215e42b338..3b177f5b4f 100644
--- a/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.cpp
+++ b/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.cpp
@@ -31,19 +31,22 @@
**************************************************************************/
#include "typespecificdeviceconfigurationlistmodel.h"
-#include "linuxdeviceconfigurations.h"
-#include "remotelinux_constants.h"
+#include "abstractembeddedlinuxtarget.h"
+
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <utils/qtcassert.h>
+
+using namespace ProjectExplorer;
namespace RemoteLinux {
namespace Internal {
-TypeSpecificDeviceConfigurationListModel::TypeSpecificDeviceConfigurationListModel(const QString &osType,
- QObject *parent) : QAbstractListModel(parent), m_targetOsType(osType)
+TypeSpecificDeviceConfigurationListModel::TypeSpecificDeviceConfigurationListModel(AbstractEmbeddedLinuxTarget *target)
+ : QAbstractListModel(target)
{
- const LinuxDeviceConfigurations * const devConfs
- = LinuxDeviceConfigurations::instance();
- connect(devConfs, SIGNAL(modelReset()), this, SIGNAL(modelReset()));
- connect(devConfs, SIGNAL(updated()), this, SIGNAL(updated()));
+ const DeviceManager * const devConfs = DeviceManager::instance();
+ connect(devConfs, SIGNAL(updated()), this, SIGNAL(modelReset()));
+ connect(target, SIGNAL(supportedDevicesChanged()), this, SIGNAL(modelReset()));
}
TypeSpecificDeviceConfigurationListModel::~TypeSpecificDeviceConfigurationListModel()
@@ -55,13 +58,10 @@ int TypeSpecificDeviceConfigurationListModel::rowCount(const QModelIndex &parent
if (parent.isValid())
return 0;
int count = 0;
- const LinuxDeviceConfigurations * const devConfs
- = LinuxDeviceConfigurations::instance();
- const int devConfsCount = devConfs->rowCount();
- if (m_targetOsType == QLatin1String(Constants::GenericLinuxOsType))
- return devConfsCount;
+ const DeviceManager * const devConfs = DeviceManager::instance();
+ const int devConfsCount = devConfs->deviceCount();
for (int i = 0; i < devConfsCount; ++i) {
- if (devConfs->deviceAt(i)->osType() == m_targetOsType)
+ if (target()->supportsDevice(devConfs->deviceAt(i)))
++count;
}
return count;
@@ -75,41 +75,46 @@ QVariant TypeSpecificDeviceConfigurationListModel::data(const QModelIndex &index
const LinuxDeviceConfiguration::ConstPtr &devConf = deviceAt(index.row());
Q_ASSERT(devConf);
QString displayedName = devConf->displayName();
- if (devConf->isDefault() && devConf->osType() == m_targetOsType)
- displayedName += QLatin1Char(' ') + tr("(default)");
+ if (target()->supportsDevice(devConf) && DeviceManager::instance()
+ ->defaultDevice(devConf->type()) == devConf) {
+ displayedName = tr("%1 (default)").arg(displayedName);
+ }
return displayedName;
}
LinuxDeviceConfiguration::ConstPtr TypeSpecificDeviceConfigurationListModel::deviceAt(int idx) const
{
int currentRow = -1;
- const LinuxDeviceConfigurations * const devConfs
- = LinuxDeviceConfigurations::instance();
- if (m_targetOsType == QLatin1String(Constants::GenericLinuxOsType))
- return devConfs->deviceAt(idx);
- const int devConfsCount = devConfs->rowCount();
+ const DeviceManager * const devConfs = DeviceManager::instance();
+ const int devConfsCount = devConfs->deviceCount();
for (int i = 0; i < devConfsCount; ++i) {
- if (devConfs->deviceAt(i)->osType() == m_targetOsType) {
- if (++currentRow == idx)
- return devConfs->deviceAt(i);
- }
+ const IDevice::ConstPtr device = devConfs->deviceAt(i);
+ if (target()->supportsDevice(device) && ++currentRow == idx)
+ return device.staticCast<const LinuxDeviceConfiguration>();
}
- Q_ASSERT(false);
- return LinuxDeviceConfiguration::ConstPtr();
+ QTC_ASSERT(false, return LinuxDeviceConfiguration::ConstPtr());
}
LinuxDeviceConfiguration::ConstPtr TypeSpecificDeviceConfigurationListModel::defaultDeviceConfig() const
{
- return LinuxDeviceConfigurations::instance()->defaultDeviceConfig(m_targetOsType);
+ const DeviceManager * const deviceManager = DeviceManager::instance();
+ const int deviceCount = deviceManager->deviceCount();
+ for (int i = 0; i < deviceCount; ++i) {
+ const IDevice::ConstPtr device = deviceManager->deviceAt(i);
+ if (target()->supportsDevice(device)
+ && deviceManager->defaultDevice(device->type()) == device) {
+ return device.staticCast<const LinuxDeviceConfiguration>();
+ }
+ }
+ return LinuxDeviceConfiguration::ConstPtr();
}
LinuxDeviceConfiguration::ConstPtr TypeSpecificDeviceConfigurationListModel::find(LinuxDeviceConfiguration::Id id) const
{
- const LinuxDeviceConfiguration::ConstPtr &devConf
- = LinuxDeviceConfigurations::instance()->find(id);
- return devConf && (devConf->osType() == m_targetOsType
- || m_targetOsType == QLatin1String(Constants::GenericLinuxOsType))
- ? devConf : defaultDeviceConfig();
+ const IDevice::ConstPtr &devConf = DeviceManager::instance()->find(id);
+ if (devConf && target()->supportsDevice(devConf))
+ return devConf.staticCast<const LinuxDeviceConfiguration>();
+ return defaultDeviceConfig();
}
int TypeSpecificDeviceConfigurationListModel::indexForInternalId(LinuxDeviceConfiguration::Id id) const
@@ -122,5 +127,10 @@ int TypeSpecificDeviceConfigurationListModel::indexForInternalId(LinuxDeviceConf
return -1;
}
+AbstractEmbeddedLinuxTarget *TypeSpecificDeviceConfigurationListModel::target() const
+{
+ return qobject_cast<AbstractEmbeddedLinuxTarget *>(QObject::parent());
+}
+
} // namespace Internal
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.h b/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.h
index 6de2cc6611..9fac87a802 100644
--- a/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.h
+++ b/src/plugins/remotelinux/typespecificdeviceconfigurationlistmodel.h
@@ -38,13 +38,14 @@
#include <QSharedPointer>
namespace RemoteLinux {
+class AbstractEmbeddedLinuxTarget;
namespace Internal {
class TypeSpecificDeviceConfigurationListModel : public QAbstractListModel
{
Q_OBJECT
public:
- explicit TypeSpecificDeviceConfigurationListModel(const QString &osType, QObject *parent = 0);
+ explicit TypeSpecificDeviceConfigurationListModel(AbstractEmbeddedLinuxTarget *target);
~TypeSpecificDeviceConfigurationListModel();
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
@@ -56,11 +57,8 @@ public:
QSharedPointer<const LinuxDeviceConfiguration> find(LinuxDeviceConfiguration::Id id) const;
int indexForInternalId(LinuxDeviceConfiguration::Id id) const;
-signals:
- void updated();
-
private:
- const QString m_targetOsType;
+ AbstractEmbeddedLinuxTarget * target() const;
};
} // namespace Internal
diff --git a/src/plugins/resourceeditor/resourceeditorplugin.h b/src/plugins/resourceeditor/resourceeditorplugin.h
index 60a5ee63af..8415d623aa 100644
--- a/src/plugins/resourceeditor/resourceeditorplugin.h
+++ b/src/plugins/resourceeditor/resourceeditorplugin.h
@@ -49,6 +49,7 @@ class ResourceEditorFactory;
class ResourceEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ResourceEditor.json")
public:
ResourceEditorPlugin();
diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h
index e348752101..aaf504a142 100644
--- a/src/plugins/subversion/subversionplugin.h
+++ b/src/plugins/subversion/subversionplugin.h
@@ -79,6 +79,7 @@ struct SubversionResponse
class SubversionPlugin : public VcsBase::VcsBasePlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Subversion.json")
public:
SubversionPlugin();
diff --git a/src/plugins/tasklist/tasklistplugin.h b/src/plugins/tasklist/tasklistplugin.h
index c264773986..57873d370d 100644
--- a/src/plugins/tasklist/tasklistplugin.h
+++ b/src/plugins/tasklist/tasklistplugin.h
@@ -47,6 +47,7 @@ class TaskListPluginPrivate;
class TaskListPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "TaskList.json")
public:
TaskListPlugin();
diff --git a/src/plugins/texteditor/autocompleter.cpp b/src/plugins/texteditor/autocompleter.cpp
index 221531a9b9..166ee33ea6 100644
--- a/src/plugins/texteditor/autocompleter.cpp
+++ b/src/plugins/texteditor/autocompleter.cpp
@@ -276,10 +276,12 @@ int AutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor,
return 0;
// verify that we indeed do have an extra opening brace in the document
+ QTextBlock block = cursor.block();
+ const QString textFromCusror = block.text().mid(cursor.positionInBlock()).trimmed();
int braceDepth = BaseTextDocumentLayout::braceDepth(doc->lastBlock());
- if (braceDepth <= 0)
- return 0; // braces are all balanced or worse, no need to do anything
+ if (braceDepth <= 0 && (textFromCusror.isEmpty() || textFromCusror.at(0) != QLatin1Char('}')))
+ return 0; // braces are all balanced or worse, no need to do anything and separator inserted not between '{' and '}'
// we have an extra brace , let's see if we should close it
@@ -289,7 +291,6 @@ int AutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor,
if (condition) {|
statement;
*/
- QTextBlock block = cursor.block();
int indentation = tabSettings.indentationColumn(block.text());
if (block.next().isValid()) { // not the last block
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index a59e2ff895..1e8632df04 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -1074,6 +1074,11 @@ void BaseTextEditorWidget::unindent()
indentOrUnindent(false);
}
+void BaseTextEditorWidget::openLinkUnderCursor()
+{
+ openLink(findLinkAt(textCursor()));
+}
+
void BaseTextEditorWidget::moveLineUpDown(bool up)
{
QTextCursor cursor = textCursor();
@@ -5838,6 +5843,11 @@ void BaseTextEditorWidget::circularPaste()
QPlainTextEdit::copy();
}
+void BaseTextEditorWidget::switchUtf8bom()
+{
+ baseTextDocument()->switchUtf8Bom();
+}
+
QMimeData *BaseTextEditorWidget::createMimeDataFromSelection() const
{
if (d->m_inBlockSelectionMode) {
@@ -6087,6 +6097,17 @@ void BaseTextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
a = am->command(Constants::CIRCULAR_PASTE)->action();
if (a && a->isEnabled())
menu->addAction(a);
+
+ BaseTextDocument *doc = baseTextDocument();
+ if (doc->codec()->name() == QString(QLatin1String("UTF-8"))) {
+ a = am->command(Constants::SWITCH_UTF8BOM)->action();
+ if (a && a->isEnabled()) {
+ a->setText(doc->format().hasUtf8Bom ? tr("Delete UTF-8 BOM on Save")
+ : tr("Add UTF-8 BOM on Save"));
+ menu->addSeparator();
+ menu->addAction(a);
+ }
+ }
}
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index dddb6bf008..45f2565f2c 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -260,6 +260,7 @@ public slots:
virtual void selectAll();
void circularPaste();
+ void switchUtf8bom();
void zoomIn(int range = 1);
void zoomOut(int range = 1);
@@ -325,6 +326,8 @@ public slots:
void indent();
void unindent();
+ void openLinkUnderCursor();
+
signals:
void changed();
diff --git a/src/plugins/texteditor/circularclipboard.cpp b/src/plugins/texteditor/circularclipboard.cpp
index 32c20ab539..bba248a84a 100644
--- a/src/plugins/texteditor/circularclipboard.cpp
+++ b/src/plugins/texteditor/circularclipboard.cpp
@@ -53,6 +53,16 @@ CircularClipboard *CircularClipboard::instance()
void CircularClipboard::collect(const QMimeData *mimeData)
{
+ //Avoid duplicates
+ const QString text = mimeData->text();
+ for (QList<const QMimeData *>::iterator i = m_items.begin(); i != m_items.end(); ++i) {
+ if (mimeData == *i || text == (*i)->text()) {
+ if (mimeData != *i)
+ delete *i;
+ m_items.erase(i);
+ break;
+ }
+ }
if (m_items.size() > kMaxSize) {
delete m_items.last();
m_items.removeLast();
diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
index 7e53d2dda9..f59ae41d85 100644
--- a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
+++ b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
@@ -512,7 +512,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::FocusOut) {
abort();
-#if defined(Q_OS_DARWIN) && ! defined(QT_MAC_USE_COCOA)
+#if (QT_VERSION < 0x050000) && defined(Q_OS_DARWIN) && ! defined(QT_MAC_USE_COCOA)
QFocusEvent *fe = static_cast<QFocusEvent *>(e);
if (fe->reason() == Qt::OtherFocusReason) {
// Qt/carbon workaround
@@ -559,7 +559,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
case Qt::Key_Tab:
case Qt::Key_Return:
-#if defined(QT_MAC_USE_COCOA) || !defined(Q_OS_DARWIN)
+#if (QT_VERSION >= 0x050000) || (defined(QT_MAC_USE_COCOA) || !defined(Q_OS_DARWIN))
abort();
if (d->m_completionListView->currentIndex().isValid())
emit proposalItemActivated(d->m_model->proposalItem(
diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs
index c12adef8c5..752535cfbf 100644
--- a/src/plugins/texteditor/texteditor.qbs
+++ b/src/plugins/texteditor/texteditor.qbs
@@ -257,5 +257,9 @@ QtcPlugin {
"tooltip/tooltip.cpp",
"tooltip/tooltip.h"
]
+ ProductModule {
+ Depends { name: "Find" }
+ Depends { name: "Locator" }
+ }
}
diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp
index 0e20f1901f..84747b7675 100644
--- a/src/plugins/texteditor/texteditoractionhandler.cpp
+++ b/src/plugins/texteditor/texteditoractionhandler.cpp
@@ -65,6 +65,7 @@ TextEditorActionHandler::TextEditorActionHandler(const char *context,
m_cutAction(0),
m_pasteAction(0),
m_circularPasteAction(0),
+ m_switchUtf8bomAction(0),
m_selectAllAction(0),
m_gotoAction(0),
m_printAction(0),
@@ -104,6 +105,8 @@ TextEditorActionHandler::TextEditorActionHandler(const char *context,
m_lowerCaseSelectionAction(0),
m_indentAction(0),
m_unindentAction(0),
+ m_followSymbolAction(0),
+ m_jumpToFileAction(0),
m_optionalActions(optionalActions),
m_currentEditor(0),
m_contextId(context),
@@ -379,6 +382,11 @@ void TextEditorActionHandler::createActions()
connect(m_circularPasteAction, SIGNAL(triggered()), this, SLOT(circularPasteAction()));
medit->addAction(command, Core::Constants::G_EDIT_COPYPASTE);
+ m_switchUtf8bomAction = new QAction(this);
+ m_modifyingActions << m_switchUtf8bomAction;
+ command = am->registerAction(m_switchUtf8bomAction, Constants::SWITCH_UTF8BOM, m_contextId, true);
+ connect(m_switchUtf8bomAction, SIGNAL(triggered()), this, SLOT(switchUtf8bomAction()));
+
m_indentAction = new QAction(tr("Indent"), this);
m_modifyingActions << m_indentAction;
command = am->registerAction(m_indentAction, Constants::INDENT, m_contextId, true);
@@ -389,6 +397,16 @@ void TextEditorActionHandler::createActions()
command = am->registerAction(m_unindentAction, Constants::UNINDENT, m_contextId, true);
connect(m_unindentAction, SIGNAL(triggered()), this, SLOT(unindent()));
+ m_followSymbolAction = new QAction(tr("Follow Symbol Under Cursor"), this);
+ command = am->registerAction(m_followSymbolAction, Constants::FOLLOW_SYMBOL_UNDER_CURSOR, m_contextId, true);
+ command->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
+ connect(m_followSymbolAction, SIGNAL(triggered()), this, SLOT(openLinkUnderCursor()));
+
+ m_jumpToFileAction = new QAction(tr("Jump To File Under Cursor"), this);
+ command = am->registerAction(m_jumpToFileAction, Constants::JUMP_TO_FILE_UNDER_CURSOR, m_contextId, true);
+ command->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
+ connect(m_jumpToFileAction, SIGNAL(triggered()), this, SLOT(openLinkUnderCursor()));
+
QAction *a = 0;
a = new QAction(tr("Go to Line Start"), this);
command = am->registerAction(a, Constants::GOTO_LINE_START, m_contextId, true);
@@ -502,6 +520,8 @@ void TextEditorActionHandler::updateActions(UpdateMode um)
a->setEnabled(um != ReadOnlyMode);
m_formatAction->setEnabled((m_optionalActions & Format) && um != ReadOnlyMode);
m_unCommentSelectionAction->setEnabled((m_optionalActions & UnCommentSelection) && um != ReadOnlyMode);
+ m_followSymbolAction->setEnabled(m_optionalActions & FollowSymbolUnderCursor);
+ m_jumpToFileAction->setEnabled(m_optionalActions & JumpToFileUnderCursor);
m_unfoldAllAction->setEnabled((m_optionalActions & UnCollapseAll));
m_visualizeWhitespaceAction->setChecked(m_currentEditor->displaySettings().m_visualizeWhitespace);
@@ -589,6 +609,7 @@ FUNCTION2(copyAction, copy)
FUNCTION2(cutAction, cut)
FUNCTION2(pasteAction, paste)
FUNCTION2(circularPasteAction, circularPaste)
+FUNCTION2(switchUtf8bomAction, switchUtf8bom)
FUNCTION2(formatAction, format)
FUNCTION2(rewrapParagraphAction, rewrapParagraph)
FUNCTION2(selectAllAction, selectAll)
@@ -625,6 +646,7 @@ FUNCTION(insertLineAbove)
FUNCTION(insertLineBelow)
FUNCTION(indent)
FUNCTION(unindent)
+FUNCTION(openLinkUnderCursor)
FUNCTION(gotoLineStart)
FUNCTION(gotoLineStartWithSelection)
diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h
index f94b384267..27f0d485ca 100644
--- a/src/plugins/texteditor/texteditoractionhandler.h
+++ b/src/plugins/texteditor/texteditoractionhandler.h
@@ -63,7 +63,9 @@ public:
None = 0,
Format = 1,
UnCommentSelection = 2,
- UnCollapseAll = 4
+ UnCollapseAll = 4,
+ FollowSymbolUnderCursor = 8,
+ JumpToFileUnderCursor = 16
};
explicit TextEditorActionHandler(const char *context, uint optionalActions = None);
@@ -99,6 +101,7 @@ private slots:
void cutAction();
void pasteAction();
void circularPasteAction();
+ void switchUtf8bomAction();
void selectAllAction();
void gotoAction();
void printAction();
@@ -140,6 +143,7 @@ private slots:
void updateCurrentEditor(Core::IEditor *editor);
void indent();
void unindent();
+ void openLinkUnderCursor();
void gotoLineStart();
void gotoLineStartWithSelection();
@@ -170,6 +174,7 @@ private:
QAction *m_cutAction;
QAction *m_pasteAction;
QAction *m_circularPasteAction;
+ QAction *m_switchUtf8bomAction;
QAction *m_selectAllAction;
QAction *m_gotoAction;
QAction *m_printAction;
@@ -210,6 +215,8 @@ private:
QAction *m_lowerCaseSelectionAction;
QAction *m_indentAction;
QAction *m_unindentAction;
+ QAction *m_followSymbolAction;
+ QAction *m_jumpToFileAction;
QList<QAction *> m_modifyingActions;
uint m_optionalActions;
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index 86108f84a5..0fc099368b 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -102,8 +102,11 @@ const char TASK_DOWNLOAD_DEFINITIONS[] = "TextEditor.Task.Download";
const char TASK_REGISTER_DEFINITIONS[] = "TextEditor.Task.Register";
const char TASK_OPEN_FILE[] = "TextEditor.Task.OpenFile";
const char CIRCULAR_PASTE[] = "TextEditor.CircularPaste";
+const char SWITCH_UTF8BOM[] = "TextEditor.SwitchUtf8bom";
const char INDENT[] = "TextEditor.Indent";
const char UNINDENT[] = "TextEditor.Unindent";
+const char FOLLOW_SYMBOL_UNDER_CURSOR[] = "TextEditor.FollowSymbolUnderCursor";
+const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor";
// Text color and style categories
const char C_TEXT[] = "Text";
diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp
index d10a75d65f..001b48948a 100644
--- a/src/plugins/texteditor/texteditorplugin.cpp
+++ b/src/plugins/texteditor/texteditorplugin.cpp
@@ -65,6 +65,8 @@
#include <QtPlugin>
#include <QMainWindow>
#include <QShortcut>
+#include <QDir>
+#include <QTemporaryFile>
using namespace TextEditor;
using namespace TextEditor::Internal;
@@ -98,6 +100,55 @@ TextEditorPlugin *TextEditorPlugin::instance()
return m_instance;
}
+static const char wizardCategoryC[] = "U.General";
+
+static inline QString wizardDisplayCategory()
+{
+ return TextEditorPlugin::tr("General");
+}
+
+// A wizard that quickly creates a scratch buffer
+// based on a temporary file without prompting for a path.
+class ScratchFileWizard : public Core::IWizard
+{
+public:
+ virtual WizardKind kind() const { return FileWizard; }
+ virtual QIcon icon() const { return QIcon(); }
+ virtual QString description() const
+ { return TextEditorPlugin::tr("Creates a scratch buffer using a temporary file."); }
+ virtual QString displayName() const
+ { return TextEditorPlugin::tr("Scratch Buffer"); }
+ virtual QString id() const
+ { return QLatin1String("Z.ScratchFile"); }
+ virtual QString category() const
+ { return QLatin1String(wizardCategoryC); }
+ virtual QString displayCategory() const
+ { return wizardDisplayCategory(); }
+ virtual QString descriptionImage() const
+ { return QString(); }
+ virtual Core::FeatureSet requiredFeatures() const
+ { return Core::FeatureSet(); }
+ virtual WizardFlags flags() const
+ { return Core::IWizard::PlatformIndependent; }
+
+ virtual void runWizard(const QString &path, QWidget *parent, const QString &platform, const QVariantMap &extraValues);
+};
+
+void ScratchFileWizard::runWizard(const QString &, QWidget *, const QString &, const QVariantMap &extraValues)
+{
+ Q_UNUSED(extraValues)
+ QString tempPattern = QDir::tempPath();
+ if (!tempPattern.endsWith(QLatin1Char('/')))
+ tempPattern += QLatin1Char('/');
+ tempPattern += QLatin1String("scratchXXXXXX.txt");
+ QTemporaryFile file(tempPattern);
+ file.setAutoRemove(false);
+ QTC_ASSERT(file.open(), return; );
+ file.close();
+ Core::EditorManager *em = Core::EditorManager::instance();
+ em->openEditor(file.fileName(), Core::Id(), Core::EditorManager::ModeSwitch);
+}
+
// ExtensionSystem::PluginInterface
bool TextEditorPlugin::initialize(const QStringList &arguments, QString *errorMessage)
{
@@ -110,14 +161,15 @@ bool TextEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
wizardParameters.setDescription(tr("Creates a text file. The default file extension is <tt>.txt</tt>. "
"You can specify a different extension as part of the filename."));
wizardParameters.setDisplayName(tr("Text File"));
- wizardParameters.setCategory(QLatin1String("U.General"));
- wizardParameters.setDisplayCategory(tr("General"));
+ wizardParameters.setCategory(QLatin1String(wizardCategoryC));
+ wizardParameters.setDisplayCategory(wizardDisplayCategory());
wizardParameters.setFlags(Core::IWizard::PlatformIndependent);
TextFileWizard *wizard = new TextFileWizard(QLatin1String(Constants::C_TEXTEDITOR_MIMETYPE_TEXT),
QLatin1String("text$"),
wizardParameters);
// Add text file wizard
addAutoReleasedObject(wizard);
+ addAutoReleasedObject(new ScratchFileWizard);
m_settings = new TextEditorSettings(this);
diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h
index 1927cf8bb0..d518a67897 100644
--- a/src/plugins/texteditor/texteditorplugin.h
+++ b/src/plugins/texteditor/texteditorplugin.h
@@ -55,6 +55,7 @@ class BaseTextMarkRegistry;
class TextEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "TextEditor.json")
public:
TextEditorPlugin();
diff --git a/src/plugins/todo/Todo.pluginspec.in b/src/plugins/todo/Todo.pluginspec.in
index edbb711cd6..ac2f43aba1 100644
--- a/src/plugins/todo/Todo.pluginspec.in
+++ b/src/plugins/todo/Todo.pluginspec.in
@@ -16,5 +16,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
</dependencyList>
</plugin>
diff --git a/src/plugins/todo/cpptodoitemsscanner.h b/src/plugins/todo/cpptodoitemsscanner.h
index dc5ff1d8da..8ce7fb208a 100644
--- a/src/plugins/todo/cpptodoitemsscanner.h
+++ b/src/plugins/todo/cpptodoitemsscanner.h
@@ -36,7 +36,7 @@
#include "todoitemsscanner.h"
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
namespace Todo {
namespace Internal {
diff --git a/src/plugins/todo/todoplugin.h b/src/plugins/todo/todoplugin.h
index efac7a2323..537d89d735 100755
--- a/src/plugins/todo/todoplugin.h
+++ b/src/plugins/todo/todoplugin.h
@@ -49,6 +49,8 @@ class TodoItem;
class TodoPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Todo.json")
+
public:
TodoPlugin();
~TodoPlugin();
diff --git a/src/plugins/updateinfo/updateinfoplugin.h b/src/plugins/updateinfo/updateinfoplugin.h
index fdc4c4c0e5..0c8511e6c6 100644
--- a/src/plugins/updateinfo/updateinfoplugin.h
+++ b/src/plugins/updateinfo/updateinfoplugin.h
@@ -45,6 +45,8 @@ class UpdateInfoPluginPrivate;
class UpdateInfoPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "UpdateInfo.json")
+
public:
UpdateInfoPlugin();
virtual ~UpdateInfoPlugin();
diff --git a/src/plugins/valgrind/callgrindtextmark.cpp b/src/plugins/valgrind/callgrindtextmark.cpp
index aa524442d9..b6b3d6fd67 100644
--- a/src/plugins/valgrind/callgrindtextmark.cpp
+++ b/src/plugins/valgrind/callgrindtextmark.cpp
@@ -80,10 +80,10 @@ void CallgrindTextMark::paint(QPainter *painter, const QRect &paintRect) const
// decrease font size if paint rect is too small (very unlikely, but may happen)
QFont font = painter->font();
- QFontMetrics fm = font;
+ QFontMetrics fm = QFontMetrics(font);
while (fm.boundingRect(text).width() > paintRect.width()) {
font.setPointSize(font.pointSize() - 1);
- fm = font;
+ fm = QFontMetrics(font);
}
painter->setFont(font);
diff --git a/src/plugins/valgrind/valgrindplugin.h b/src/plugins/valgrind/valgrindplugin.h
index 937ce38d96..9aa9e9c946 100644
--- a/src/plugins/valgrind/valgrindplugin.h
+++ b/src/plugins/valgrind/valgrindplugin.h
@@ -44,6 +44,7 @@ namespace Internal {
class ValgrindPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Valgrind.json")
public:
ValgrindPlugin() {}
diff --git a/src/plugins/valgrind/valgrindrunner.cpp b/src/plugins/valgrind/valgrindrunner.cpp
index cdc84cfd3f..25a0c8aad0 100644
--- a/src/plugins/valgrind/valgrindrunner.cpp
+++ b/src/plugins/valgrind/valgrindrunner.cpp
@@ -53,8 +53,7 @@ public:
process(0),
channelMode(QProcess::SeparateChannels),
finished(false),
- startMode(Analyzer::StartLocal),
- connParams(Utils::SshConnectionParameters::DefaultProxy)
+ startMode(Analyzer::StartLocal)
{
}
diff --git a/src/plugins/vcsbase/VcsBase.pluginspec.in b/src/plugins/vcsbase/VcsBase.pluginspec.in
index 276ae7e0dd..9d0844c05b 100644
--- a/src/plugins/vcsbase/VcsBase.pluginspec.in
+++ b/src/plugins/vcsbase/VcsBase.pluginspec.in
@@ -17,5 +17,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
</dependencyList>
</plugin>
diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs
index 3db7e2c69b..f7e239c4ff 100644
--- a/src/plugins/vcsbase/vcsbase.qbs
+++ b/src/plugins/vcsbase/vcsbase.qbs
@@ -11,6 +11,7 @@ QtcPlugin {
Depends { name: "Find" }
Depends { name: "cpp" }
Depends { name: "qt"; submodules: ['gui'] }
+ Depends { name: "CppTools" }
Depends { name: "CPlusPlus" }
cpp.includePaths: [
diff --git a/src/plugins/vcsbase/vcsbase_dependencies.pri b/src/plugins/vcsbase/vcsbase_dependencies.pri
index 672a4f65cf..3f3f1c378f 100644
--- a/src/plugins/vcsbase/vcsbase_dependencies.pri
+++ b/src/plugins/vcsbase/vcsbase_dependencies.pri
@@ -2,4 +2,4 @@ include(../../plugins/coreplugin/coreplugin.pri)
include(../../plugins/texteditor/texteditor.pri)
include(../../plugins/projectexplorer/projectexplorer.pri)
include(../../plugins/find/find.pri)
-include(../../libs/cplusplus/cplusplus.pri)
+include(../../plugins/cpptools/cpptools.pri)
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index 07d9df8a9e..d9244961f9 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -952,11 +952,11 @@ void VcsBaseEditorWidget::mouseMoveEvent(QMouseEvent *e)
handler->highlightCurrentContents();
overrideCursor = true;
cursorShape = Qt::PointingHandCursor;
+ } else {
+ setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
+ overrideCursor = true;
+ cursorShape = Qt::IBeamCursor;
}
- } else {
- setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
- overrideCursor = true;
- cursorShape = Qt::IBeamCursor;
}
TextEditor::BaseTextEditorWidget::mouseMoveEvent(e);
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
index 38edfae954..41360a5458 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
@@ -43,7 +43,7 @@
#include <cplusplus/CoreTypes.h>
#include <cplusplus/FullySpecifiedType.h>
#include <cplusplus/Literals.h>
-#include <cplusplus/ModelManagerInterface.h>
+#include <cpptools/ModelManagerInterface.h>
#include <cplusplus/Symbol.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/TranslationUnit.h>
diff --git a/src/plugins/vcsbase/vcsplugin.h b/src/plugins/vcsbase/vcsplugin.h
index 0147ece563..d60f13c2c1 100644
--- a/src/plugins/vcsbase/vcsplugin.h
+++ b/src/plugins/vcsbase/vcsplugin.h
@@ -51,6 +51,7 @@ class CoreListener;
class VcsPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "VcsBase.json")
public:
VcsPlugin();
diff --git a/src/plugins/welcome/welcome.pro b/src/plugins/welcome/welcome.pro
index 482506916b..57296515bf 100644
--- a/src/plugins/welcome/welcome.pro
+++ b/src/plugins/welcome/welcome.pro
@@ -1,6 +1,11 @@
TEMPLATE = lib
TARGET = Welcome
-QT += network declarative
+QT += network
+greaterThan(QT_MAJOR_VERSION, 4) {
+ QT += quick1
+} else {
+ QT += declarative
+}
include(../../qtcreatorplugin.pri)
include(welcome_dependencies.pri)
diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp
index 6fa140612f..50c1739064 100644
--- a/src/plugins/welcome/welcomeplugin.cpp
+++ b/src/plugins/welcome/welcomeplugin.cpp
@@ -209,10 +209,35 @@ void WelcomeMode::initPlugins()
QDeclarativeContext *ctx = m_welcomePage->rootContext();
ctx->setContextProperty(QLatin1String("welcomeMode"), this);
- QList<Utils::IWelcomePage*> plugins = PluginManager::instance()->getObjects<Utils::IWelcomePage>();
- qSort(plugins.begin(), plugins.end(), &sortFunction);
+ QList<Utils::IWelcomePage*> duplicatePlugins = PluginManager::instance()->getObjects<Utils::IWelcomePage>();
+ qSort(duplicatePlugins.begin(), duplicatePlugins.end(), &sortFunction);
+
+ QList<Utils::IWelcomePage*> plugins;
+ QHash<Utils::IWelcomePage::Id, Utils::IWelcomePage*> pluginHash;
+
+ //avoid duplicate ids - choose by priority
+ foreach (Utils::IWelcomePage* plugin, duplicatePlugins) {
+ if (pluginHash.contains(plugin->id())) {
+ Utils::IWelcomePage* pluginOther = pluginHash.value(plugin->id());
+
+ if (pluginOther->priority() > plugin->priority()) {
+ plugins.removeAll(pluginOther);
+ pluginHash.remove(pluginOther->id());
+ plugins << plugin;
+ pluginHash.insert(plugin->id(), plugin);
+ }
+
+ } else {
+ plugins << plugin;
+ pluginHash.insert(plugin->id(), plugin);
+ }
+ }
+
QDeclarativeEngine *engine = m_welcomePage->engine();
+ QStringList importPathList = engine->importPathList();
+ importPathList << Core::ICore::resourcePath() + QLatin1String("/welcomescreen");
+ engine->setImportPathList(importPathList);
if (!debug)
engine->setOutputWarningsToStandardError(false);
engine->setNetworkAccessManagerFactory(new NetworkAccessManagerFactory);
@@ -253,7 +278,24 @@ QString WelcomeMode::platform() const
void WelcomeMode::welcomePluginAdded(QObject *obj)
{
+ QHash<Utils::IWelcomePage::Id, Utils::IWelcomePage*> pluginHash;
+
+ foreach (QObject *obj, m_pluginList) {
+ Utils::IWelcomePage *plugin = qobject_cast<Utils::IWelcomePage*>(obj);
+ pluginHash.insert(plugin->id(), plugin);
+ }
if (Utils::IWelcomePage *plugin = qobject_cast<Utils::IWelcomePage*>(obj)) {
+ //check for duplicated id
+ if (pluginHash.contains(plugin->id())) {
+ Utils::IWelcomePage* pluginOther = pluginHash.value(plugin->id());
+
+ if (pluginOther->priority() > plugin->priority()) {
+ m_pluginList.removeAll(pluginOther);
+ } else {
+ return;
+ }
+ }
+
int insertPos = 0;
foreach (Utils::IWelcomePage* p, PluginManager::instance()->getObjects<Utils::IWelcomePage>()) {
if (plugin->priority() < p->priority())
diff --git a/src/plugins/welcome/welcomeplugin.h b/src/plugins/welcome/welcomeplugin.h
index 68664e3103..32cfb79e9f 100644
--- a/src/plugins/welcome/welcomeplugin.h
+++ b/src/plugins/welcome/welcomeplugin.h
@@ -47,6 +47,8 @@ class WelcomeMode;
class WelcomePlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Welcome.json")
+
public:
WelcomePlugin();
diff --git a/src/qtcreatorplugin.pri b/src/qtcreatorplugin.pri
index 1e659e6ab8..53ceac018b 100644
--- a/src/qtcreatorplugin.pri
+++ b/src/qtcreatorplugin.pri
@@ -57,6 +57,22 @@ copy2build.name = COPY ${QMAKE_FILE_IN}
copy2build.CONFIG += no_link
QMAKE_EXTRA_COMPILERS += copy2build
+greaterThan(QT_MAJOR_VERSION, 4) {
+# Create a Json file containing the plugin information required by
+# Qt 5's plugin system by running a XSLT sheet on the
+# pluginspec file before moc runs.
+ XMLPATTERNS = $$targetPath($$[QT_INSTALL_BINS]/xmlpatterns)
+
+ pluginspec2json.name = Create Qt 5 plugin json file
+ pluginspec2json.input = PLUGINSPEC
+ pluginspec2json.variable_out = GENERATED_FILES
+ pluginspec2json.output = $${TARGET}.json
+ pluginspec2json.commands = $$XMLPATTERNS -no-format -output $$pluginspec2json.output $$PWD/pluginjsonmetadata.xsl $$PLUGINSPEC
+ pluginspec2json.CONFIG += no_link
+ moc_header.depends += $$pluginspec2json.output
+ QMAKE_EXTRA_COMPILERS += pluginspec2json
+}
+
macx {
!isEmpty(TIGER_COMPAT_MODE) {
QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../PlugIns/$${PROVIDER}/
diff --git a/src/shared/help/bookmarkmanager.cpp b/src/shared/help/bookmarkmanager.cpp
index 38f2c01280..c6364596bf 100644
--- a/src/shared/help/bookmarkmanager.cpp
+++ b/src/shared/help/bookmarkmanager.cpp
@@ -32,9 +32,7 @@
#include "bookmarkmanager.h"
-#include "centralwidget.h"
#include "localhelpmanager.h"
-#include "openpagesmanager.h"
#include <utils/filterlineedit.h>
#include <utils/styledbar.h>
@@ -406,7 +404,7 @@ void BookmarkWidget::customContextMenuRequested(const QPoint &point)
emit linkActivated(data);
}
else if (pickedAction == showItemNewTab) {
- OpenPagesManager::instance().createPage(data);
+ emit createPage(QUrl(data), false);
}
else if (pickedAction == removeItem) {
bookmarkManager->removeBookmarkItem(treeView,
@@ -569,7 +567,7 @@ bool BookmarkWidget::eventFilter(QObject *object, QEvent *e)
|| (me->button() == Qt::MidButton)) {
QString data = index.data(Qt::UserRole + 10).toString();
if (!data.isEmpty() && data != QLatin1String("Folder"))
- OpenPagesManager::instance().createPage(data);
+ emit createPage(QUrl(data), false);
}
}
}
diff --git a/src/shared/help/bookmarkmanager.h b/src/shared/help/bookmarkmanager.h
index 98b22d3b6a..445b8f7f50 100644
--- a/src/shared/help/bookmarkmanager.h
+++ b/src/shared/help/bookmarkmanager.h
@@ -119,6 +119,7 @@ public:
signals:
void addBookmark();
void linkActivated(const QUrl &url);
+ void createPage(const QUrl &url, bool fromSearch);
private slots:
void removeClicked();
diff --git a/src/tools/mdnssd/CommonServices.h b/src/tools/mdnssd/CommonServices.h
new file mode 100644
index 0000000000..1261f1d5ab
--- /dev/null
+++ b/src/tools/mdnssd/CommonServices.h
@@ -0,0 +1,1518 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header CommonServices
+
+ Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE.
+*/
+
+#ifndef __COMMON_SERVICES__
+#define __COMMON_SERVICES__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if 0
+#pragma mark == Target ==
+#endif
+
+//===========================================================================================================================
+// Target
+//===========================================================================================================================
+
+// Macintosh
+
+#if( !defined( TARGET_OS_MAC ) )
+ #if( ( macintosh || __MACH__ ) && !KERNEL )
+ // ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #define TARGET_OS_MAC 0
+ #endif
+#endif
+
+#if( !defined( TARGET_API_MAC_OSX_KERNEL ) )
+ #if( __MACH__ && KERNEL )
+ #define TARGET_API_MAC_OSX_KERNEL 1
+ #else
+ #define TARGET_API_MAC_OSX_KERNEL 0
+ #endif
+#endif
+
+// Linux
+
+#if( !defined( TARGET_OS_LINUX ) )
+ #if( defined( __linux__ ) )
+ #define TARGET_OS_LINUX 1
+ #else
+ #define TARGET_OS_LINUX 0
+ #endif
+#endif
+
+// Solaris
+
+#if( !defined( TARGET_OS_SOLARIS ) )
+ #if( defined(solaris) || (defined(__SVR4) && defined(sun)) )
+ #define TARGET_OS_SOLARIS 1
+ #else
+ #define TARGET_OS_SOLARIS 0
+ #endif
+#endif
+
+// Palm
+
+#if( !defined( TARGET_OS_PALM ) )
+ #if( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) )
+ #define TARGET_OS_PALM 1
+ #else
+ #define TARGET_OS_PALM 0
+ #endif
+#endif
+
+// VxWorks
+
+#if( !defined( TARGET_OS_VXWORKS ) )
+
+ // No predefined macro for VxWorks so just assume VxWorks if nothing else is set.
+
+ #if( !macintosh && !__MACH__ && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) )
+ #define TARGET_OS_VXWORKS 1
+ #else
+ #define TARGET_OS_VXWORKS 0
+ #endif
+#endif
+
+// Windows
+
+#if( !defined( TARGET_OS_WIN32 ) )
+ #if( macintosh || __MACH__ )
+ // ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #if( defined( _WIN32 ) )
+ #define TARGET_OS_WIN32 1
+ #else
+ #define TARGET_OS_WIN32 0
+ #endif
+ #endif
+#endif
+
+// Windows CE
+
+#if( !defined( TARGET_OS_WINDOWS_CE ) )
+ #if( defined( _WIN32_WCE ) )
+ #define TARGET_OS_WINDOWS_CE 1
+ #else
+ #define TARGET_OS_WINDOWS_CE 0
+ #endif
+#endif
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+ #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF)
+ #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+ #endif
+ #include <stddef.h>
+#endif
+
+#if( ( macintosh || __MACH__ ) && !KERNEL )
+
+ #if( defined( __MWERKS__ ) )
+ #if( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+ #else
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #if( __MACH__ )
+
+ // Mac OS X
+
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <fcntl.h>
+ #include <pthread.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <unistd.h>
+
+ #else
+
+ // Classic Mac OS
+
+ #include <ConditionalMacros.h>
+ #include <MacTypes.h>
+
+ #endif
+
+#elif( KERNEL )
+
+ // Mac OS X Kernel
+
+ #include <stdint.h>
+
+ #include <libkern/OSTypes.h>
+ #include <sys/types.h>
+
+#elif( TARGET_OS_LINUX )
+
+ // Linux
+
+ #include <stdint.h>
+ #include <arpa/inet.h>
+
+#elif( TARGET_OS_SOLARIS )
+
+ // Solaris
+
+ #include <stdint.h>
+
+ #include <arpa/inet.h>
+ #include <arpa/nameser.h>
+
+ #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+ #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+
+#elif( TARGET_OS_PALM )
+
+ // Palm (no special includes yet).
+
+#elif( TARGET_OS_VXWORKS )
+
+ // VxWorks
+
+ #include "vxWorks.h"
+
+#elif( TARGET_OS_WIN32 )
+
+ // Windows
+
+ #if( !defined( WIN32_WINDOWS ) )
+ #define WIN32_WINDOWS 0x0401
+ #endif
+
+ #if( !defined( _WIN32_WINDOWS ) )
+ #define _WIN32_WINDOWS 0x0401
+ #endif
+
+ #if( !defined( WIN32_LEAN_AND_MEAN ) )
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+ #endif
+
+ #if( defined( __MWERKS__ ) )
+
+ #if( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #elif( defined( _MSC_VER ) )
+
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+ #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers.
+
+ #endif
+
+ #include <windows.h>
+ #include <winsock2.h>
+ #include <Ws2tcpip.h>
+
+ #if( defined( _MSC_VER ) )
+ #pragma warning( default:4706 )
+ #endif
+
+#else
+ #error unknown OS - update this file to support your OS
+#endif
+
+#if( !defined( TARGET_BUILD_MAIN ) )
+ #if( !TARGET_OS_VXWORKS )
+ #define TARGET_BUILD_MAIN 1
+ #endif
+#endif
+
+#if( __GNUC__ || !TARGET_OS_VXWORKS )
+ #define TARGET_LANGUAGE_C_LIKE 1
+#else
+ #define TARGET_LANGUAGE_C_LIKE 0
+#endif
+
+#if 0
+#pragma mark == CPU ==
+#endif
+
+//===========================================================================================================================
+// CPU
+//===========================================================================================================================
+
+// PowerPC
+
+#if( !defined( TARGET_CPU_PPC ) )
+ #if( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) )
+ #define TARGET_CPU_PPC 1
+ #else
+ #define TARGET_CPU_PPC 0
+ #endif
+#endif
+
+// x86
+
+#if( !defined( TARGET_CPU_X86 ) )
+ #if( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) )
+ #define TARGET_CPU_X86 1
+ #else
+ #define TARGET_CPU_X86 0
+ #endif
+#endif
+
+// MIPS
+
+#if( !defined( TARGET_CPU_MIPS ) )
+ #if( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) )
+ #define TARGET_CPU_MIPS 1
+ #else
+ #define TARGET_CPU_MIPS 0
+ #endif
+#endif
+
+#if( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) )
+ #error unknown CPU - update this file to support your CPU
+#endif
+
+#if 0
+#pragma mark == Byte Order ==
+#endif
+
+//===========================================================================================================================
+// Byte Order
+//===========================================================================================================================
+
+// TARGET_RT_LITTLE_ENDIAN
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \
+ TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #endif
+#endif
+
+// TARGET_RT_BIG_ENDIAN
+
+#if( !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \
+ ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #else
+ #define TARGET_RT_BIG_ENDIAN 0
+ #endif
+#endif
+
+#if( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BIG_ENDIAN 0
+ #else
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+#endif
+
+#if( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if( TARGET_RT_BIG_ENDIAN )
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+#endif
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) )
+ #error unknown byte order - update this file to support your byte order
+#endif
+
+// TARGET_RT_BYTE_ORDER
+
+#if( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER ) )
+ #if( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN
+ #else
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN
+ #endif
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#if( !TARGET_OS_MAC )
+ #define CR '\r'
+#endif
+
+#define LF '\n'
+#define CRSTR "\r"
+#define LFSTR "\n"
+#define CRLF "\r\n"
+#define CRCR "\r\r"
+
+#if 0
+#pragma mark == Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Compatibility
+//===========================================================================================================================
+
+// Macros to allow the same code to work on Windows and other sockets API-compatible platforms.
+
+#if( TARGET_OS_WIN32 )
+ #define close_compat( X ) closesocket( X )
+ #define errno_compat() (int) GetLastError()
+ #define set_errno_compat( X ) SetLastError( X )
+ #define EWOULDBLOCK_compat WSAEWOULDBLOCK
+ #define ETIMEDOUT_compat WSAETIMEDOUT
+ #define ENOTCONN_compat WSAENOTCONN
+ #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET )
+ #define kInvalidSocketRef INVALID_SOCKET
+ #if( TARGET_LANGUAGE_C_LIKE )
+ typedef SOCKET SocketRef;
+ #endif
+#else
+ #define close_compat( X ) close( X )
+ #define errno_compat() errno
+ #define set_errno_compat( X ) do { errno = ( X ); } while( 0 )
+ #define EWOULDBLOCK_compat EWOULDBLOCK
+ #define ETIMEDOUT_compat ETIMEDOUT
+ #define ENOTCONN_compat ENOTCONN
+ #define IsValidSocket( X ) ( ( X ) >= 0 )
+ #define kInvalidSocketRef -1
+ #if( TARGET_LANGUAGE_C_LIKE )
+ typedef int SocketRef;
+ #endif
+#endif
+
+// socklen_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that.
+// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that.
+// - VxWorks
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS )
+ typedef int socklen_t;
+ #endif
+#endif
+
+// ssize_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Mac OS X when not building with BSD headers
+// - Windows
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC)
+ typedef int ssize_t;
+ #endif
+#endif
+
+// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure.
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !defined( AF_INET6 ) )
+ #define sockaddr_storage sockaddr_in
+ #define ss_family sin_family
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LOOPBACK
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \
+ ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \
+ : 0
+#else
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : 0
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LINK_LOCAL
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) )
+#else
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : 0 )
+#endif
+
+// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking
+// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to
+// CreateThread on Windows CE.
+
+#if( TARGET_OS_WINDOWS_CE )
+ #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \
+ (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \
+ (LPDWORD) THREAD_ID_PTR )
+
+ #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT )
+#elif( TARGET_OS_WIN32 )
+ #define _beginthreadex_compat _beginthreadex
+ #define _endthreadex_compat _endthreadex
+#endif
+
+// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed.
+
+#if( defined( _MSC_VER ) )
+ #define inline_compat __inline
+#else
+ #define inline_compat inline
+#endif
+
+// Calling conventions
+
+#if( !defined( CALLBACK_COMPAT ) )
+ #if( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE )
+ #define CALLBACK_COMPAT CALLBACK
+ #else
+ #define CALLBACK_COMPAT
+ #endif
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kSizeCString
+
+ @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen.
+*/
+
+#define kSizeCString ( (size_t) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_array
+
+ @abstract Determines the number of elements in an array.
+*/
+
+#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_element
+
+ @abstract Determines the size of an array element.
+*/
+
+#define sizeof_element( X ) sizeof( X[ 0 ] )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_string
+
+ @abstract Determines the size of a constant C string, excluding the null terminator.
+*/
+
+#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_field
+
+ @abstract Determines the size of a field of a type.
+*/
+
+#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function RoundUp
+
+ @abstract Rounds X up to a multiple of Y.
+*/
+
+#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) - ( ( X ) % ( Y ) ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsAligned
+
+ @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define IsAligned( X, Y ) ( ( ( X ) & ( ( Y ) - 1 ) ) == 0 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsFieldAligned
+
+ @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignDown
+
+ @abstract Aligns X down to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define AlignDown( X, Y ) ( ( X ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignUp
+
+ @abstract Aligns X up to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) - 1 ) ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Min
+
+ @abstract Returns the lesser of X and Y.
+*/
+
+#if( !defined( Min ) )
+ #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Max
+
+ @abstract Returns the greater of X and Y.
+*/
+
+#if( !defined( Max ) )
+ #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function InsertBits
+
+ @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK.
+
+ For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value:
+
+ InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000
+*/
+
+#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) & ~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function ExtractBits
+
+ @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift right to right justify MASK.
+
+ For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example):
+
+ ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3
+*/
+
+#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Stringify
+
+ @abstract Stringify's an expression.
+
+ @discussion
+
+ Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary
+ because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the
+ -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise,
+ the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines).
+
+ For example:
+
+ #define kMyConstant 1
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1"
+
+ Non-preprocessor symbols do not have this issue. For example:
+
+ enum
+ {
+ kMyConstant = 1
+ };
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant"
+
+ See <http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html> for more info on C preprocessor pre-scanning.
+*/
+
+#define Stringify( X ) # X
+#define StringifyExpansion( X ) Stringify( X )
+
+#if 0
+#pragma mark == Types ==
+#endif
+
+#if( TARGET_LANGUAGE_C_LIKE )
+//===========================================================================================================================
+// Standard Types
+//===========================================================================================================================
+
+#if( !defined( INT8_MIN ) )
+
+ #define INT8_MIN SCHAR_MIN
+
+ #if( defined( _MSC_VER ) )
+
+ // C99 stdint.h not supported in VC++/VS.NET yet.
+
+ typedef INT8 int8_t;
+ typedef UINT8 uint8_t;
+ typedef INT16 int16_t;
+ typedef UINT16 uint16_t;
+ typedef INT32 int32_t;
+ typedef UINT32 uint32_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+
+ #elif( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) )
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+ #endif
+
+ typedef int8_t int_least8_t;
+ typedef int16_t int_least16_t;
+ typedef int32_t int_least32_t;
+ typedef int64_t int_least64_t;
+
+ typedef uint8_t uint_least8_t;
+ typedef uint16_t uint_least16_t;
+ typedef uint32_t uint_least32_t;
+ typedef uint64_t uint_least64_t;
+
+ typedef int8_t int_fast8_t;
+ typedef int16_t int_fast16_t;
+ typedef int32_t int_fast32_t;
+ typedef int64_t int_fast64_t;
+
+ typedef uint8_t uint_fast8_t;
+ typedef uint16_t uint_fast16_t;
+ typedef uint32_t uint_fast32_t;
+ typedef uint64_t uint_fast64_t;
+
+ #if( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE )
+ typedef long int intptr_t;
+ typedef unsigned long int uintptr_t;
+ #endif
+
+#endif
+
+// Macros for minimum-width integer constants
+
+#if( !defined( INT8_C ) )
+ #define INT8_C( value ) value
+#endif
+
+#if( !defined( INT16_C ) )
+ #define INT16_C( value ) value
+#endif
+
+#if( !defined( INT32_C ) )
+ #define INT32_C( value ) value ## L
+#endif
+
+#if( !defined( INT64_C ) )
+ #if( defined( _MSC_VER ) )
+ #define INT64_C( value ) value ## i64
+ #else
+ #define INT64_C( value ) value ## LL
+ #endif
+#endif
+
+#if( !defined( UINT8_C ) )
+ #define UINT8_C( value ) value ## U
+#endif
+
+#if( !defined( UINT16_C ) )
+ #define UINT16_C( value ) value ## U
+#endif
+
+#if( !defined( UINT32_C ) )
+ #define UINT32_C( value ) value ## UL
+#endif
+
+#if( !defined( UINT64_C ) )
+ #if( defined( _MSC_VER ) )
+ #define UINT64_C( value ) value ## UI64
+ #else
+ #define UINT64_C( value ) value ## ULL
+ #endif
+#endif
+
+#if 0
+#pragma mark == bool ==
+#endif
+
+//===========================================================================================================================
+// Boolean Constants and Types
+//===========================================================================================================================
+
+// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though.
+// C99 defines __bool_true_false_are_defined when bool, true, and false are defined.
+// MacTypes.h defines true and false (Mac builds only).
+//
+// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely
+// short-circuit and gets confused by the option( bool ) portion of the conditional.
+
+#if( defined( __MWERKS__ ) )
+
+ // Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line.
+
+ #if( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) )
+ #define COMMON_SERVICES_NEEDS_BOOL 1
+ #else
+ #define COMMON_SERVICES_NEEDS_BOOL 0
+ #endif
+
+ // Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool.
+
+ #if( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) )
+ #define _Bool int
+ #endif
+
+ // Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header,
+ // which defines true and false to map to C++ true and false (which are not enabled). Serenity Now!
+
+ #if( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) )
+ #define true 1
+ #define false 0
+ #endif
+#else
+ #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined )
+#endif
+
+#if( COMMON_SERVICES_NEEDS_BOOL )
+
+ typedef int bool;
+
+ #define bool bool
+
+ #if( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) )
+ #define true 1
+ #define false 0
+ #endif
+
+ #define __bool_true_false_are_defined 1
+#endif
+
+// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h.
+
+#if( TARGET_API_MAC_OSX_KERNEL )
+ #define TYPE_BOOL 1
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef CStr255
+
+ @abstract 255 character null-terminated (C-style) string.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ typedef char CStr255[ 256 ];
+#endif
+
+#endif // TARGET_LANGUAGE_C_LIKE
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined TYPE_LONGLONG_NATIVE
+
+ @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries.
+*/
+
+#if( !defined( TYPE_LONGLONG_NATIVE ) )
+ #if( !TARGET_OS_VXWORKS )
+ #define TYPE_LONGLONG_NATIVE 1
+ #else
+ #define TYPE_LONGLONG_NATIVE 0
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined long_long_compat
+
+ @abstract Compatibility type to map to the closest thing to long long and unsigned long long.
+
+ @discussion
+
+ Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary
+ "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( TARGET_OS_WIN32 )
+ typedef __int64 long_long_compat;
+ typedef unsigned __int64 unsigned_long_long_compat;
+ #else
+ typedef signed long long long_long_compat;
+ typedef unsigned long long unsigned_long_long_compat;
+ #endif
+#endif
+
+#if 0
+#pragma mark == Errors ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum OSStatus
+
+ @abstract Status Code
+
+ @constant kNoErr 0 No error occurred.
+ @constant kInProgressErr 1 Operation in progress.
+ @constant kUnknownErr -6700 Unknown error occurred.
+ @constant kOptionErr -6701 Option was not acceptable.
+ @constant kSelectorErr -6702 Selector passed in is invalid or unknown.
+ @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time).
+ @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable.
+ @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate.
+ @constant kParamCountErr -6706 Incorrect or unsupported number of parameters.
+ @constant kCommandErr -6707 Command invalid or not supported.
+ @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier.
+ @constant kStateErr -6709 Not in appropriate state to perform operation.
+ @constant kRangeErr -6710 Index is out of range or not valid.
+ @constant kRequestErr -6711 Request was improperly formed or not appropriate.
+ @constant kResponseErr -6712 Response was incorrect or out of sequence.
+ @constant kChecksumErr -6713 Checksum does not match the actual data.
+ @constant kNotHandledErr -6714 Operation was not handled (or not handled completely).
+ @constant kVersionErr -6715 Version is not incorrect or not compatibile.
+ @constant kSignatureErr -6716 Signature did not match what was expected.
+ @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format.
+ @constant kNotInitializedErr -6718 Action request before needed services were initialized.
+ @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized.
+ @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use).
+ @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks).
+ @constant kTimeoutErr -6722 Timeout occurred.
+ @constant kCanceledErr -6723 Operation canceled (successful cancel).
+ @constant kAlreadyCanceledErr -6724 Operation has already been canceled.
+ @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid).
+ @constant kDeletedErr -6726 Object has already been deleted.
+ @constant kNotFoundErr -6727 Something was not found.
+ @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation.
+ @constant kNoResourcesErr -6729 Resources unavailable to perform the operation.
+ @constant kDuplicateErr -6730 Duplicate found or something is a duplicate.
+ @constant kImmutableErr -6731 Entity is not changeable.
+ @constant kUnsupportedDataErr -6732 Data is unknown or not supported.
+ @constant kIntegrityErr -6733 Data is corrupt.
+ @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format.
+ @constant kUnsupportedErr -6735 Feature or option is not supported.
+ @constant kUnexpectedErr -6736 Error occurred that was not expected.
+ @constant kValueErr -6737 Value is not appropriate.
+ @constant kNotReadableErr -6738 Could not read or reading is not allowed.
+ @constant kNotWritableErr -6739 Could not write or writing is not allowed.
+ @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified.
+ @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified.
+ @constant kMalformedErr -6742 Something was not formed correctly.
+ @constant kSizeErr -6743 Size was too big, too small, or not appropriate.
+ @constant kNameErr -6744 Name was not correct, allowed, or appropriate.
+ @constant kNotReadyErr -6745 Device or service is not ready.
+ @constant kReadErr -6746 Could not read.
+ @constant kWriteErr -6747 Could not write.
+ @constant kMismatchErr -6748 Something does not match.
+ @constant kDateErr -6749 Date is invalid or out-of-range.
+ @constant kUnderrunErr -6750 Less data than expected.
+ @constant kOverrunErr -6751 More data than expected.
+ @constant kEndingErr -6752 Connection, session, or something is ending.
+ @constant kConnectionErr -6753 Connection failed or could not be established.
+ @constant kAuthenticationErr -6754 Authentication failed or is not supported.
+ @constant kOpenErr -6755 Could not open file, pipe, device, etc.
+ @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.).
+ @constant kSkipErr -6757 Items should be or was skipped.
+ @constant kNoAckErr -6758 No acknowledge.
+ @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time).
+ @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed.
+ @constant kNoAddressAckErr -6761 No acknowledge of address.
+ @constant kBusyErr -6762 Cannot perform because something is busy.
+ @constant kNoSpaceErr -6763 Not enough space to perform operation.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL )
+ typedef int32_t OSStatus;
+ #endif
+#endif
+
+#define kNoErr 0
+#define kInProgressErr 1
+
+// Generic error codes are in the range -6700 to -6779.
+
+#define kGenericErrorBase -6700 // Starting error code for all generic errors.
+
+#define kUnknownErr -6700
+#define kOptionErr -6701
+#define kSelectorErr -6702
+#define kExecutionStateErr -6703
+#define kPathErr -6704
+#define kParamErr -6705
+#define kParamCountErr -6706
+#define kCommandErr -6707
+#define kIDErr -6708
+#define kStateErr -6709
+#define kRangeErr -6710
+#define kRequestErr -6711
+#define kResponseErr -6712
+#define kChecksumErr -6713
+#define kNotHandledErr -6714
+#define kVersionErr -6715
+#define kSignatureErr -6716
+#define kFormatErr -6717
+#define kNotInitializedErr -6718
+#define kAlreadyInitializedErr -6719
+#define kNotInUseErr -6720
+#define kInUseErr -6721
+#define kTimeoutErr -6722
+#define kCanceledErr -6723
+#define kAlreadyCanceledErr -6724
+#define kCannotCancelErr -6725
+#define kDeletedErr -6726
+#define kNotFoundErr -6727
+#define kNoMemoryErr -6728
+#define kNoResourcesErr -6729
+#define kDuplicateErr -6730
+#define kImmutableErr -6731
+#define kUnsupportedDataErr -6732
+#define kIntegrityErr -6733
+#define kIncompatibleErr -6734
+#define kUnsupportedErr -6735
+#define kUnexpectedErr -6736
+#define kValueErr -6737
+#define kNotReadableErr -6738
+#define kNotWritableErr -6739
+#define kBadReferenceErr -6740
+#define kFlagErr -6741
+#define kMalformedErr -6742
+#define kSizeErr -6743
+#define kNameErr -6744
+#define kNotReadyErr -6745
+#define kReadErr -6746
+#define kWriteErr -6747
+#define kMismatchErr -6748
+#define kDateErr -6749
+#define kUnderrunErr -6750
+#define kOverrunErr -6751
+#define kEndingErr -6752
+#define kConnectionErr -6753
+#define kAuthenticationErr -6754
+#define kOpenErr -6755
+#define kTypeErr -6756
+#define kSkipErr -6757
+#define kNoAckErr -6758
+#define kCollisionErr -6759
+#define kBackoffErr -6760
+#define kNoAddressAckErr -6761
+#define kBusyErr -6762
+#define kNoSpaceErr -6763
+
+#define kGenericErrorEnd -6779 // Last generic error code (inclusive)
+
+#if 0
+#pragma mark == Mac Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Mac Compatibility
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum Duration
+
+ @abstract Type used to specify a duration of time.
+
+ @constant kDurationImmediate Indicates no delay/wait time.
+ @constant kDurationMicrosecond Microsecond units.
+ @constant kDurationMillisecond Millisecond units.
+ @constant kDurationSecond Second units.
+ @constant kDurationMinute Minute units.
+ @constant kDurationHour Hour units.
+ @constant kDurationDay Day units.
+ @constant kDurationForever Infinite period of time (no timeout).
+
+ @discussion
+
+ Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example,
+ to wait for 5 seconds you would use "5 * kDurationSecond".
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ #if( !TARGET_OS_MAC )
+ typedef int32_t Duration;
+ #endif
+#endif
+
+#define kDurationImmediate 0L
+#define kDurationMicrosecond -1L
+#define kDurationMillisecond 1L
+#define kDurationSecond ( 1000L * kDurationMillisecond )
+#define kDurationMinute ( 60L * kDurationSecond )
+#define kDurationHour ( 60L * kDurationMinute )
+#define kDurationDay ( 24L * kDurationHour )
+#define kDurationForever 0x7FFFFFFFL
+
+// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions
+
+#define kNanosecondsPerMicrosecond 1000
+#define kNanosecondsPerMillisecond 1000000
+#define kNanosecondsPerSecond 1000000000
+#define kMicrosecondsPerSecond 1000000
+#define kMicrosecondsPerMillisecond 1000
+#define kMillisecondsPerSecond 1000
+#define kSecondsPerMinute 60
+#define kSecondsPerHour ( 60 * 60 ) // 3600
+#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400
+#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800
+#define kMinutesPerHour 60
+#define kMinutesPerDay ( 60 * 24 ) // 1440
+#define kHoursPerDay 24
+#define kDaysPerWeek 7
+#define kWeeksPerYear 52
+#define kMonthsPerYear 12
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined VersionStages
+
+ @abstract NumVersion-style version stages.
+*/
+
+#define kVersionStageDevelopment 0x20
+#define kVersionStageAlpha 0x40
+#define kVersionStageBeta 0x60
+#define kVersionStageFinal 0x80
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionBuild
+
+ @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4).
+*/
+
+#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \
+ ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \
+ ( ( ( MINOR ) & 0x0F ) << 20 ) | \
+ ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \
+ ( ( ( STAGE ) & 0xFF ) << 8 ) | \
+ ( ( ( REV ) & 0xFF ) ) )
+
+#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) )
+#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) )
+#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) )
+#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) )
+#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) )
+#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionCompare
+
+ @abstract Compares two NumVersion values and returns the following values:
+
+ left < right -> -1
+ left > right -> 1
+ left = right -> 0
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+ int NumVersionCompare( uint32_t inLeft, uint32_t inRight );
+#endif
+
+#if 0
+#pragma mark == Binary Constants ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_4
+
+ @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA).
+*/
+
+#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) )
+#define binary_4_hex_wrap( a ) binary_4_hex( a )
+#define binary_4_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_8
+
+ @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B).
+*/
+
+#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) )
+#define binary_8_hex_wrap( a ) binary_8_hex( a )
+#define binary_8_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_16
+
+ @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B).
+*/
+
+#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) )
+#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b )
+#define binary_16_hex( a, b ) ( 0x ## a ## b )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_32
+
+ @abstract Macro to generate an 32-bit constant using binary notation
+ (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B).
+*/
+
+#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) )
+#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d )
+#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d )
+
+// Binary Constant Helpers
+
+#define hex_digit8( a ) HEX_DIGIT_ ## a
+#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a
+
+#define HEX_DIGIT_00000000 00
+#define HEX_DIGIT_00000001 01
+#define HEX_DIGIT_00000010 02
+#define HEX_DIGIT_00000011 03
+#define HEX_DIGIT_00000100 04
+#define HEX_DIGIT_00000101 05
+#define HEX_DIGIT_00000110 06
+#define HEX_DIGIT_00000111 07
+#define HEX_DIGIT_00001000 08
+#define HEX_DIGIT_00001001 09
+#define HEX_DIGIT_00001010 0A
+#define HEX_DIGIT_00001011 0B
+#define HEX_DIGIT_00001100 0C
+#define HEX_DIGIT_00001101 0D
+#define HEX_DIGIT_00001110 0E
+#define HEX_DIGIT_00001111 0F
+#define HEX_DIGIT_00010000 10
+#define HEX_DIGIT_00010001 11
+#define HEX_DIGIT_00010010 12
+#define HEX_DIGIT_00010011 13
+#define HEX_DIGIT_00010100 14
+#define HEX_DIGIT_00010101 15
+#define HEX_DIGIT_00010110 16
+#define HEX_DIGIT_00010111 17
+#define HEX_DIGIT_00011000 18
+#define HEX_DIGIT_00011001 19
+#define HEX_DIGIT_00011010 1A
+#define HEX_DIGIT_00011011 1B
+#define HEX_DIGIT_00011100 1C
+#define HEX_DIGIT_00011101 1D
+#define HEX_DIGIT_00011110 1E
+#define HEX_DIGIT_00011111 1F
+#define HEX_DIGIT_00100000 20
+#define HEX_DIGIT_00100001 21
+#define HEX_DIGIT_00100010 22
+#define HEX_DIGIT_00100011 23
+#define HEX_DIGIT_00100100 24
+#define HEX_DIGIT_00100101 25
+#define HEX_DIGIT_00100110 26
+#define HEX_DIGIT_00100111 27
+#define HEX_DIGIT_00101000 28
+#define HEX_DIGIT_00101001 29
+#define HEX_DIGIT_00101010 2A
+#define HEX_DIGIT_00101011 2B
+#define HEX_DIGIT_00101100 2C
+#define HEX_DIGIT_00101101 2D
+#define HEX_DIGIT_00101110 2E
+#define HEX_DIGIT_00101111 2F
+#define HEX_DIGIT_00110000 30
+#define HEX_DIGIT_00110001 31
+#define HEX_DIGIT_00110010 32
+#define HEX_DIGIT_00110011 33
+#define HEX_DIGIT_00110100 34
+#define HEX_DIGIT_00110101 35
+#define HEX_DIGIT_00110110 36
+#define HEX_DIGIT_00110111 37
+#define HEX_DIGIT_00111000 38
+#define HEX_DIGIT_00111001 39
+#define HEX_DIGIT_00111010 3A
+#define HEX_DIGIT_00111011 3B
+#define HEX_DIGIT_00111100 3C
+#define HEX_DIGIT_00111101 3D
+#define HEX_DIGIT_00111110 3E
+#define HEX_DIGIT_00111111 3F
+#define HEX_DIGIT_01000000 40
+#define HEX_DIGIT_01000001 41
+#define HEX_DIGIT_01000010 42
+#define HEX_DIGIT_01000011 43
+#define HEX_DIGIT_01000100 44
+#define HEX_DIGIT_01000101 45
+#define HEX_DIGIT_01000110 46
+#define HEX_DIGIT_01000111 47
+#define HEX_DIGIT_01001000 48
+#define HEX_DIGIT_01001001 49
+#define HEX_DIGIT_01001010 4A
+#define HEX_DIGIT_01001011 4B
+#define HEX_DIGIT_01001100 4C
+#define HEX_DIGIT_01001101 4D
+#define HEX_DIGIT_01001110 4E
+#define HEX_DIGIT_01001111 4F
+#define HEX_DIGIT_01010000 50
+#define HEX_DIGIT_01010001 51
+#define HEX_DIGIT_01010010 52
+#define HEX_DIGIT_01010011 53
+#define HEX_DIGIT_01010100 54
+#define HEX_DIGIT_01010101 55
+#define HEX_DIGIT_01010110 56
+#define HEX_DIGIT_01010111 57
+#define HEX_DIGIT_01011000 58
+#define HEX_DIGIT_01011001 59
+#define HEX_DIGIT_01011010 5A
+#define HEX_DIGIT_01011011 5B
+#define HEX_DIGIT_01011100 5C
+#define HEX_DIGIT_01011101 5D
+#define HEX_DIGIT_01011110 5E
+#define HEX_DIGIT_01011111 5F
+#define HEX_DIGIT_01100000 60
+#define HEX_DIGIT_01100001 61
+#define HEX_DIGIT_01100010 62
+#define HEX_DIGIT_01100011 63
+#define HEX_DIGIT_01100100 64
+#define HEX_DIGIT_01100101 65
+#define HEX_DIGIT_01100110 66
+#define HEX_DIGIT_01100111 67
+#define HEX_DIGIT_01101000 68
+#define HEX_DIGIT_01101001 69
+#define HEX_DIGIT_01101010 6A
+#define HEX_DIGIT_01101011 6B
+#define HEX_DIGIT_01101100 6C
+#define HEX_DIGIT_01101101 6D
+#define HEX_DIGIT_01101110 6E
+#define HEX_DIGIT_01101111 6F
+#define HEX_DIGIT_01110000 70
+#define HEX_DIGIT_01110001 71
+#define HEX_DIGIT_01110010 72
+#define HEX_DIGIT_01110011 73
+#define HEX_DIGIT_01110100 74
+#define HEX_DIGIT_01110101 75
+#define HEX_DIGIT_01110110 76
+#define HEX_DIGIT_01110111 77
+#define HEX_DIGIT_01111000 78
+#define HEX_DIGIT_01111001 79
+#define HEX_DIGIT_01111010 7A
+#define HEX_DIGIT_01111011 7B
+#define HEX_DIGIT_01111100 7C
+#define HEX_DIGIT_01111101 7D
+#define HEX_DIGIT_01111110 7E
+#define HEX_DIGIT_01111111 7F
+#define HEX_DIGIT_10000000 80
+#define HEX_DIGIT_10000001 81
+#define HEX_DIGIT_10000010 82
+#define HEX_DIGIT_10000011 83
+#define HEX_DIGIT_10000100 84
+#define HEX_DIGIT_10000101 85
+#define HEX_DIGIT_10000110 86
+#define HEX_DIGIT_10000111 87
+#define HEX_DIGIT_10001000 88
+#define HEX_DIGIT_10001001 89
+#define HEX_DIGIT_10001010 8A
+#define HEX_DIGIT_10001011 8B
+#define HEX_DIGIT_10001100 8C
+#define HEX_DIGIT_10001101 8D
+#define HEX_DIGIT_10001110 8E
+#define HEX_DIGIT_10001111 8F
+#define HEX_DIGIT_10010000 90
+#define HEX_DIGIT_10010001 91
+#define HEX_DIGIT_10010010 92
+#define HEX_DIGIT_10010011 93
+#define HEX_DIGIT_10010100 94
+#define HEX_DIGIT_10010101 95
+#define HEX_DIGIT_10010110 96
+#define HEX_DIGIT_10010111 97
+#define HEX_DIGIT_10011000 98
+#define HEX_DIGIT_10011001 99
+#define HEX_DIGIT_10011010 9A
+#define HEX_DIGIT_10011011 9B
+#define HEX_DIGIT_10011100 9C
+#define HEX_DIGIT_10011101 9D
+#define HEX_DIGIT_10011110 9E
+#define HEX_DIGIT_10011111 9F
+#define HEX_DIGIT_10100000 A0
+#define HEX_DIGIT_10100001 A1
+#define HEX_DIGIT_10100010 A2
+#define HEX_DIGIT_10100011 A3
+#define HEX_DIGIT_10100100 A4
+#define HEX_DIGIT_10100101 A5
+#define HEX_DIGIT_10100110 A6
+#define HEX_DIGIT_10100111 A7
+#define HEX_DIGIT_10101000 A8
+#define HEX_DIGIT_10101001 A9
+#define HEX_DIGIT_10101010 AA
+#define HEX_DIGIT_10101011 AB
+#define HEX_DIGIT_10101100 AC
+#define HEX_DIGIT_10101101 AD
+#define HEX_DIGIT_10101110 AE
+#define HEX_DIGIT_10101111 AF
+#define HEX_DIGIT_10110000 B0
+#define HEX_DIGIT_10110001 B1
+#define HEX_DIGIT_10110010 B2
+#define HEX_DIGIT_10110011 B3
+#define HEX_DIGIT_10110100 B4
+#define HEX_DIGIT_10110101 B5
+#define HEX_DIGIT_10110110 B6
+#define HEX_DIGIT_10110111 B7
+#define HEX_DIGIT_10111000 B8
+#define HEX_DIGIT_10111001 B9
+#define HEX_DIGIT_10111010 BA
+#define HEX_DIGIT_10111011 BB
+#define HEX_DIGIT_10111100 BC
+#define HEX_DIGIT_10111101 BD
+#define HEX_DIGIT_10111110 BE
+#define HEX_DIGIT_10111111 BF
+#define HEX_DIGIT_11000000 C0
+#define HEX_DIGIT_11000001 C1
+#define HEX_DIGIT_11000010 C2
+#define HEX_DIGIT_11000011 C3
+#define HEX_DIGIT_11000100 C4
+#define HEX_DIGIT_11000101 C5
+#define HEX_DIGIT_11000110 C6
+#define HEX_DIGIT_11000111 C7
+#define HEX_DIGIT_11001000 C8
+#define HEX_DIGIT_11001001 C9
+#define HEX_DIGIT_11001010 CA
+#define HEX_DIGIT_11001011 CB
+#define HEX_DIGIT_11001100 CC
+#define HEX_DIGIT_11001101 CD
+#define HEX_DIGIT_11001110 CE
+#define HEX_DIGIT_11001111 CF
+#define HEX_DIGIT_11010000 D0
+#define HEX_DIGIT_11010001 D1
+#define HEX_DIGIT_11010010 D2
+#define HEX_DIGIT_11010011 D3
+#define HEX_DIGIT_11010100 D4
+#define HEX_DIGIT_11010101 D5
+#define HEX_DIGIT_11010110 D6
+#define HEX_DIGIT_11010111 D7
+#define HEX_DIGIT_11011000 D8
+#define HEX_DIGIT_11011001 D9
+#define HEX_DIGIT_11011010 DA
+#define HEX_DIGIT_11011011 DB
+#define HEX_DIGIT_11011100 DC
+#define HEX_DIGIT_11011101 DD
+#define HEX_DIGIT_11011110 DE
+#define HEX_DIGIT_11011111 DF
+#define HEX_DIGIT_11100000 E0
+#define HEX_DIGIT_11100001 E1
+#define HEX_DIGIT_11100010 E2
+#define HEX_DIGIT_11100011 E3
+#define HEX_DIGIT_11100100 E4
+#define HEX_DIGIT_11100101 E5
+#define HEX_DIGIT_11100110 E6
+#define HEX_DIGIT_11100111 E7
+#define HEX_DIGIT_11101000 E8
+#define HEX_DIGIT_11101001 E9
+#define HEX_DIGIT_11101010 EA
+#define HEX_DIGIT_11101011 EB
+#define HEX_DIGIT_11101100 EC
+#define HEX_DIGIT_11101101 ED
+#define HEX_DIGIT_11101110 EE
+#define HEX_DIGIT_11101111 EF
+#define HEX_DIGIT_11110000 F0
+#define HEX_DIGIT_11110001 F1
+#define HEX_DIGIT_11110010 F2
+#define HEX_DIGIT_11110011 F3
+#define HEX_DIGIT_11110100 F4
+#define HEX_DIGIT_11110101 F5
+#define HEX_DIGIT_11110110 F6
+#define HEX_DIGIT_11110111 F7
+#define HEX_DIGIT_11111000 F8
+#define HEX_DIGIT_11111001 F9
+#define HEX_DIGIT_11111010 FA
+#define HEX_DIGIT_11111011 FB
+#define HEX_DIGIT_11111100 FC
+#define HEX_DIGIT_11111101 FD
+#define HEX_DIGIT_11111110 FE
+#define HEX_DIGIT_11111111 FF
+
+#if 0
+#pragma mark == Debugging ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function CommonServicesTest
+
+ @abstract Unit test.
+*/
+
+#if( DEBUG )
+ #if( TARGET_LANGUAGE_C_LIKE )
+ OSStatus CommonServicesTest( void );
+ #endif
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __COMMON_SERVICES__
diff --git a/src/tools/mdnssd/DebugServices.c b/src/tools/mdnssd/DebugServices.c
new file mode 100644
index 0000000000..647329628c
--- /dev/null
+++ b/src/tools/mdnssd/DebugServices.c
@@ -0,0 +1,3075 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To Do:
+
+ - Use StackWalk on Windows to optionally print stack frames.
+*/
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+ #include <ctype.h>
+ #include <stdio.h>
+ #include <string.h>
+#endif
+
+#include "CommonServices.h"
+
+#include "DebugServices.h"
+
+#if( DEBUG )
+
+#if( TARGET_OS_VXWORKS )
+ #include "intLib.h"
+#endif
+
+#if( TARGET_OS_WIN32 )
+ #include <time.h>
+
+ #if( !TARGET_OS_WINDOWS_CE )
+ #include <fcntl.h>
+ #include <io.h>
+ #endif
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL )
+ #include <IOKit/IOLib.h>
+#endif
+
+// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h.
+
+#if( defined( MDNS_DEBUGMSGS ) )
+ #include "mDNSEmbeddedAPI.h"
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) )
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize );
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+ static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename );
+ static void DebugFPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// iDebug (Mac OS X user and kernel)
+
+#if( DEBUG_IDEBUG_ENABLED )
+ static OSStatus DebugiDebugInit( void );
+ static void DebugiDebugPrint( char *inData, size_t inSize );
+#endif
+
+// kprintf (Mac OS X Kernel)
+
+#if( DEBUG_KPRINTF_ENABLED )
+ static void DebugKPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X IOLog (Mac OS X Kernel)
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ static void DebugMacOSXIOLogPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X Log
+
+#if( TARGET_OS_MAC )
+ static OSStatus DebugMacOSXLogInit( void );
+ static void DebugMacOSXLogPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Debugger
+
+#if( TARGET_OS_WIN32 )
+ static void DebugWindowsDebuggerPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Event Log
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule );
+ static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize );
+#endif
+
+// DebugLib support
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ static pascal void
+ DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertionString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg );
+#endif
+
+// Utilities
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString );
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static void DebugWinEnableConsole( void );
+#endif
+
+#if( TARGET_OS_WIN32 )
+ static TCHAR *
+ DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount );
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Private Globals
+//===========================================================================================================================
+
+#if( TARGET_OS_VXWORKS )
+ // TCP States for inetstatShow.
+
+ extern char ** pTcpstates; // defined in tcpLib.c
+
+ const char * kDebugTCPStates[] =
+ {
+ "(0) TCPS_CLOSED",
+ "(1) TCPS_LISTEN",
+ "(2) TCPS_SYN_SENT",
+ "(3) TCPS_SYN_RECEIVED",
+ "(4) TCPS_ESTABLISHED",
+ "(5) TCPS_CLOSE_WAIT",
+ "(6) TCPS_FIN_WAIT_1",
+ "(7) TCPS_CLOSING",
+ "(8) TCPS_LAST_ACK",
+ "(9) TCPS_FIN_WAIT_2",
+ "(10) TCPS_TIME_WAIT",
+ };
+#endif
+
+// General
+
+static bool gDebugInitialized = false;
+static DebugOutputType gDebugOutputType = kDebugOutputTypeNone;
+static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo;
+static DebugLevel gDebugPrintLevelMax = kDebugLevelMax;
+static DebugLevel gDebugBreakLevel = kDebugLevelAssert;
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL;
+#endif
+
+// Custom
+
+static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL;
+static void * gDebugCustomOutputContext = NULL;
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+ static FILE * gDebugFPrintFFile = NULL;
+#endif
+
+// MacOSXLog
+
+#if( TARGET_OS_MAC )
+ typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... );
+
+ static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL;
+#endif
+
+// WindowsEventLog
+
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ static HANDLE gDebugWindowsEventLogEventSource = NULL;
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == General ==
+#endif
+
+//===========================================================================================================================
+// DebugInitialize
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... )
+{
+ OSStatus err;
+ DebugOutputType type;
+ va_list args;
+
+ va_start( args, inType );
+
+#if( TARGET_OS_VXWORKS )
+ // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason).
+
+ if( !pTcpstates )
+ {
+ pTcpstates = (char **) kDebugTCPStates;
+ }
+#endif
+
+ // Set up DebugLib stuff (if building with Debugging.h).
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ if( !gDebugAssertOutputHandlerUPP )
+ {
+ gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler );
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP );
+ }
+ }
+#endif
+
+ // Pre-process meta-output kind to pick an appropriate output kind for the platform.
+
+ type = inType;
+ if( type == kDebugOutputTypeMetaConsole )
+ {
+ #if( TARGET_OS_MAC )
+ type = kDebugOutputTypeMacOSXLog;
+ #elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ type = kDebugOutputTypeWindowsDebugger;
+ #endif
+ #elif( TARGET_API_MAC_OSX_KERNEL )
+ #if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ type = kDebugOutputTypeMacOSXIOLog;
+ #elif( DEBUG_IDEBUG_ENABLED )
+ type = kDebugOutputTypeiDebug;
+ #elif( DEBUG_KPRINTF_ENABLED )
+ type = kDebugOutputTypeKPrintF;
+ #endif
+ #elif( TARGET_OS_VXWORKS )
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ #error target is VxWorks, but fprintf output is disabled
+ #endif
+ #else
+ #if( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #endif
+ #endif
+ }
+
+ // Process output kind.
+
+ gDebugOutputType = type;
+ switch( type )
+ {
+ case kDebugOutputTypeNone:
+ err = kNoErr;
+ break;
+
+ case kDebugOutputTypeCustom:
+ gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr );
+ gDebugCustomOutputContext = va_arg( args, void * );
+ err = kNoErr;
+ break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ if( inType == kDebugOutputTypeMetaConsole )
+ {
+ err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL );
+ }
+ else
+ {
+ DebugOutputTypeFlags flags;
+ const char * filename;
+
+ flags = (DebugOutputTypeFlags) va_arg( args, unsigned int );
+ if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile )
+ {
+ filename = va_arg( args, const char * );
+ }
+ else
+ {
+ filename = NULL;
+ }
+ err = DebugFPrintFInit( flags, filename );
+ }
+ break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ err = DebugiDebugInit();
+ break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ err = kNoErr;
+ break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ err = kNoErr;
+ break;
+#endif
+
+#if( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ err = DebugMacOSXLogInit();
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ err = kNoErr;
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ {
+ const char * name;
+ HMODULE module;
+
+ name = va_arg( args, const char * );
+ module = va_arg( args, HMODULE );
+ err = DebugWindowsEventLogInit( name, module );
+ }
+ break;
+#endif
+
+ default:
+ err = kParamErr;
+ goto exit;
+ }
+ gDebugInitialized = true;
+
+exit:
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFinalize
+//===========================================================================================================================
+
+DEBUG_EXPORT void DebugFinalize( void )
+{
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( NULL );
+ DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP );
+ gDebugAssertOutputHandlerUPP = NULL;
+ }
+#endif
+}
+
+//===========================================================================================================================
+// DebugGetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel * level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMin;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMax;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugBreakLevel;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugSetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMin = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMax = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel );
+ gDebugBreakLevel = level;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Output ==
+#endif
+
+//===========================================================================================================================
+// DebugPrintF
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... )
+{
+ va_list args;
+ size_t n;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ va_start( args, inFormat );
+ n = DebugPrintFVAList( inLevel, inFormat, args );
+ va_end( args );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrintFVAList
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs )
+{
+ size_t n;
+ char buffer[ 512 ];
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs );
+ DebugPrint( inLevel, buffer, (size_t) n );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrint
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ OSStatus err;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ err = kRangeErr;
+ goto exit;
+ }
+
+ // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available).
+
+ if( DebugTaskLevel() & kDebugInterruptLevelMask )
+ {
+ #if( TARGET_OS_VXWORKS )
+ logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 );
+ #endif
+
+ err = kExecutionStateErr;
+ goto exit;
+ }
+
+ // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage).
+
+ if( !gDebugInitialized )
+ {
+ debug_initialize( kDebugOutputTypeMetaConsole );
+ }
+
+ // Print based on the current output type.
+
+ switch( gDebugOutputType )
+ {
+ case kDebugOutputTypeNone:
+ break;
+
+ case kDebugOutputTypeCustom:
+ if( gDebugCustomOutputFunction )
+ {
+ gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext );
+ }
+ break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ DebugFPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ DebugiDebugPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ DebugKPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ DebugMacOSXIOLogPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ DebugMacOSXLogPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ DebugWindowsDebuggerPrint( inData, inSize );
+ break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ DebugWindowsEventLogPrint( inLevel, inData, inSize );
+ break;
+#endif
+
+ default:
+ break;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugPrintAssert
+//
+// Warning: This routine relies on several of the strings being string constants that will exist forever because the
+// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based
+// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant
+// constant strings, but if this function is invoked directly from other places, it must use constant strings.
+//===========================================================================================================================
+
+DEBUG_EXPORT void
+ DebugPrintAssert(
+ int_least32_t inErrorCode,
+ const char * inAssertString,
+ const char * inMessage,
+ const char * inFilename,
+ int_least32_t inLineNumber,
+ const char * inFunction )
+{
+ // Skip if the level is not in the enabled range..
+
+ if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) )
+ {
+ return;
+ }
+
+ if( inErrorCode != 0 )
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] error: %ld (%m)\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inErrorCode, inErrorCode,
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+ else
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] assert: \"%s\" %s\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inAssertString ? inAssertString : "",
+ inMessage ? inMessage : "",
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+
+ // Break into the debugger if enabled.
+
+ #if( TARGET_OS_WIN32 )
+ if( gDebugBreakLevel <= kDebugLevelAssert )
+ {
+ if( IsDebuggerPresent() )
+ {
+ DebugBreak();
+ }
+ }
+ #endif
+}
+
+#if 0
+#pragma mark -
+#endif
+
+#if( DEBUG_FPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugFPrintFInit
+//===========================================================================================================================
+
+static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename )
+{
+ OSStatus err;
+ DebugOutputTypeFlags typeFlags;
+
+ typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask;
+ if( typeFlags == kDebugOutputTypeFlagsStdOut )
+ {
+ #if( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsStdErr )
+ {
+ #if( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsFile )
+ {
+ require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr );
+
+ gDebugFPrintFFile = fopen( inFilename, "a" );
+ require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr );
+ }
+ else
+ {
+ err = kParamErr;
+ goto exit;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFPrintFPrint
+//===========================================================================================================================
+
+static void DebugFPrintFPrint( char *inData, size_t inSize )
+{
+ char * p;
+ char * q;
+
+ // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform.
+
+ p = inData;
+ q = p + inSize;
+ while( p < q )
+ {
+ if( *p == '\r' )
+ {
+ *p = '\n';
+ }
+ ++p;
+ }
+
+ // Write the data and flush.
+
+ if( gDebugFPrintFFile )
+ {
+ fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData );
+ fflush( gDebugFPrintFFile );
+ }
+}
+#endif // DEBUG_FPRINTF_ENABLED
+
+#if( DEBUG_IDEBUG_ENABLED )
+//===========================================================================================================================
+// DebugiDebugInit
+//===========================================================================================================================
+
+static OSStatus DebugiDebugInit( void )
+{
+ OSStatus err;
+
+ #if( TARGET_API_MAC_OSX_KERNEL )
+
+ extern uint32_t * _giDebugReserved1;
+
+ // Emulate the iDebugSetOutputType macro in iDebugServices.h.
+ // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext.
+
+ if( !_giDebugReserved1 )
+ {
+ _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) );
+ require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr );
+ }
+ *_giDebugReserved1 = 0x00010000U;
+ err = kNoErr;
+exit:
+ #else
+
+ __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType );
+
+ iDebugSetOutputTypeInternal( 0x00010000U );
+ err = kNoErr;
+
+ #endif
+
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugiDebugPrint
+//===========================================================================================================================
+
+static void DebugiDebugPrint( char *inData, size_t inSize )
+{
+ #if( TARGET_API_MAC_OSX_KERNEL )
+
+ // Locally declared here so we do not need to include iDebugKext.h.
+ // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the
+ // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present).
+ // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present.
+
+ typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ extern iDebugLogFunctionPtr _giDebugLogInternal;
+
+ if( _giDebugLogInternal )
+ {
+ _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+ }
+
+ #else
+
+ __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+
+ #endif
+}
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugKPrintFPrint
+//===========================================================================================================================
+
+static void DebugKPrintFPrint( char *inData, size_t inSize )
+{
+ extern void kprintf( const char *inFormat, ... );
+
+ kprintf( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+//===========================================================================================================================
+// DebugMacOSXIOLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXIOLogPrint( char *inData, size_t inSize )
+{
+ extern void IOLog( const char *inFormat, ... );
+
+ IOLog( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( TARGET_OS_MAC )
+//===========================================================================================================================
+// DebugMacOSXLogInit
+//===========================================================================================================================
+
+static OSStatus DebugMacOSXLogInit( void )
+{
+ OSStatus err;
+ CFStringRef path;
+ CFURLRef url;
+ CFBundleRef bundle;
+ CFStringRef functionName;
+ void * functionPtr;
+
+ bundle = NULL;
+
+ // Create a bundle reference for System.framework.
+
+ path = CFSTR( "/System/Library/Frameworks/System.framework" );
+ url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true );
+ require_action_quiet( url, exit, err = memFullErr );
+
+ bundle = CFBundleCreate( NULL, url );
+ CFRelease( url );
+ require_action_quiet( bundle, exit, err = memFullErr );
+
+ // Get a ptr to the system's "printf" function from System.framework.
+
+ functionName = CFSTR( "printf" );
+ functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName );
+ require_action_quiet( functionPtr, exit, err = memFullErr );
+
+ // Success! Note: The bundle cannot be released because it would invalidate the function ptr.
+
+ gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr;
+ bundle = NULL;
+ err = noErr;
+
+exit:
+ if( bundle )
+ {
+ CFRelease( bundle );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugMacOSXLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXLogPrint( char *inData, size_t inSize )
+{
+ if( gDebugMacOSXLogFunction )
+ {
+ gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData );
+ }
+}
+#endif
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWindowsDebuggerPrint
+//===========================================================================================================================
+
+void DebugWindowsDebuggerPrint( char *inData, size_t inSize )
+{
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Print out the string to the debugger.
+
+ OutputDebugString( buffer );
+}
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWindowsEventLogInit
+//===========================================================================================================================
+
+static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule )
+{
+ OSStatus err;
+ HKEY key;
+ TCHAR name[ 128 ];
+ const char * src;
+ TCHAR path[ MAX_PATH ];
+ size_t size;
+ DWORD typesSupported;
+ DWORD n;
+
+ key = NULL;
+
+ // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds.
+
+ if( !inName || ( *inName == '\0' ) )
+ {
+ inName = "DefaultApp";
+ }
+ DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL );
+
+ // Build the path string using the fixed registry path and app name.
+
+ src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
+ DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size );
+ DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL );
+
+ // Add/Open the source name as a sub-key under the Application key in the EventLog registry key.
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL );
+ require_noerr_quiet( err, exit );
+
+ // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator.
+
+ n = GetModuleFileName( inModule, path, sizeof_array( path ) );
+ err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+ n += 1;
+ n *= sizeof( TCHAR );
+
+ err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+ require_noerr_quiet( err, exit );
+
+ // Set the supported event types in the TypesSupported subkey.
+
+ typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE |
+ EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
+ err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+ require_noerr_quiet( err, exit );
+
+ // Set up the event source.
+
+ gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name );
+ err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+
+exit:
+ if( key )
+ {
+ RegCloseKey( key );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugWindowsEventLogPrint
+//===========================================================================================================================
+
+static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ WORD type;
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+ const TCHAR * array[ 1 ];
+
+ // Map the debug level to a Windows EventLog type.
+
+ if( inLevel <= kDebugLevelNotice )
+ {
+ type = EVENTLOG_INFORMATION_TYPE;
+ }
+ else if( inLevel <= kDebugLevelWarning )
+ {
+ type = EVENTLOG_WARNING_TYPE;
+ }
+ else
+ {
+ type = EVENTLOG_ERROR_TYPE;
+ }
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Add the the string to the event log.
+
+ array[ 0 ] = buffer;
+ if( gDebugWindowsEventLogEventSource )
+ {
+ ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL );
+ }
+}
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+//===========================================================================================================================
+// DebugAssertOutputHandler
+//===========================================================================================================================
+
+static pascal void
+ DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg )
+{
+ DEBUG_UNUSED( inComponentSignature );
+ DEBUG_UNUSED( inOptions );
+ DEBUG_UNUSED( inExceptionString );
+ DEBUG_UNUSED( inValue );
+ DEBUG_UNUSED( inOutputMsg );
+
+ DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+// DebugSNPrintF
+//
+// Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes:
+//
+// Changed names to avoid name collisions with the mDNS versions.
+// Changed types to standard C types since mDNSEmbeddedAPI.h may not be available.
+// Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.h.
+// Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+// Added %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+// Added %.8a - FIbre Channel address. Arg=ptr to address.
+// Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+// Added %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+// Added %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+// Added %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc.
+// Added %S - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr.
+// Added %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...)
+ {
+ size_t length;
+
+ va_list ptr;
+ va_start(ptr,fmt);
+ length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr);
+ va_end(ptr);
+
+ return(length);
+ }
+
+//===========================================================================================================================
+// DebugSNPrintFVAList - va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg)
+ {
+ static const struct DebugSNPrintF_format
+ {
+ unsigned leftJustify : 1;
+ unsigned forceSign : 1;
+ unsigned zeroPad : 1;
+ unsigned havePrecision : 1;
+ unsigned hSize : 1;
+ char lSize;
+ char altForm;
+ char sign; // +, - or space
+ unsigned int fieldWidth;
+ unsigned int precision;
+ } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ size_t nwritten = 0;
+ int c;
+ if (buflen == 0) return(0);
+ buflen--; // Pre-reserve one space in the buffer for the terminating nul
+ if (buflen == 0) goto exit;
+
+ for (c = *fmt; c != 0; c = *++fmt)
+ {
+ if (c != '%')
+ {
+ *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ }
+ else
+ {
+ size_t i=0, j;
+ // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+ // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+ // The size needs to be enough for a 256-byte domain name plus some error text.
+ #define mDNS_VACB_Size 300
+ char mDNS_VACB[mDNS_VACB_Size];
+ #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+ #define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s))
+ char *s = mDNS_VACB_Lim;
+ const char *digits = "0123456789ABCDEF";
+ struct DebugSNPrintF_format F = DebugSNPrintF_format_default;
+
+ for(;;) // decode flags
+ {
+ c = *++fmt;
+ if (c == '-') F.leftJustify = 1;
+ else if (c == '+') F.forceSign = 1;
+ else if (c == ' ') F.sign = ' ';
+ else if (c == '#') F.altForm++;
+ else if (c == '0') F.zeroPad = 1;
+ else break;
+ }
+
+ if (c == '*') // decode field width
+ {
+ int f = va_arg(arg, int);
+ if (f < 0) { f = -f; F.leftJustify = 1; }
+ F.fieldWidth = (unsigned int)f;
+ c = *++fmt;
+ }
+ else
+ {
+ for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+ }
+
+ if (c == '.') // decode precision
+ {
+ if ((c = *++fmt) == '*')
+ { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+ else for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.precision = (10 * F.precision) + (c - '0');
+ F.havePrecision = 1;
+ }
+
+ if (F.leftJustify) F.zeroPad = 0;
+
+ conv:
+ switch (c) // perform appropriate conversion
+ {
+ #if TYPE_LONGLONG_NATIVE
+ unsigned_long_long_compat n;
+ unsigned_long_long_compat base;
+ #else
+ unsigned long n;
+ unsigned long base;
+ #endif
+ case 'h' : F.hSize = 1; c = *++fmt; goto conv;
+ case 'l' : // fall through
+ case 'L' : F.lSize++; c = *++fmt; goto conv;
+ case 'd' :
+ case 'i' : base = 10;
+ goto canBeSigned;
+ case 'u' : base = 10;
+ goto notSigned;
+ case 'o' : base = 8;
+ goto notSigned;
+ case 'b' : base = 2;
+ goto notSigned;
+ case 'p' : n = va_arg(arg, uintptr_t);
+ F.havePrecision = 1;
+ F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16;
+ F.sign = 0;
+ base = 16;
+ c = 'x';
+ goto number;
+ case 'x' : digits = "0123456789abcdef";
+ case 'X' : base = 16;
+ goto notSigned;
+ canBeSigned:
+ #if TYPE_LONGLONG_NATIVE
+ if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long);
+ else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat);
+ else n = (unsigned_long_long_compat)va_arg(arg, int);
+ #else
+ if (F.lSize == 1) n = (unsigned long)va_arg(arg, long);
+ else if (F.lSize == 2) goto exit;
+ else n = (unsigned long)va_arg(arg, int);
+ #endif
+ if (F.hSize) n = (short) n;
+ #if TYPE_LONGLONG_NATIVE
+ if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; }
+ #else
+ if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+ #endif
+ else if (F.forceSign) F.sign = '+';
+ goto number;
+
+ notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long);
+ else if (F.lSize == 2)
+ {
+ #if TYPE_LONGLONG_NATIVE
+ n = va_arg(arg, unsigned_long_long_compat);
+ #else
+ goto exit;
+ #endif
+ }
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ F.sign = 0;
+ goto number;
+
+ number: if (!F.havePrecision)
+ {
+ if (F.zeroPad)
+ {
+ F.precision = F.fieldWidth;
+ if (F.altForm) F.precision -= 2;
+ if (F.sign) --F.precision;
+ }
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]);
+ for (; i < F.precision; i++) *--s = '0';
+ if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+ if (F.sign) { *--s = F.sign; i++; }
+ break;
+
+ case 'a' : {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ char pre[4] = "";
+ char post[32] = "";
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (F.altForm == 1)
+ {
+ #if(defined(MDNS_DEBUGMSGS))
+ mDNSAddr *ip = (mDNSAddr*)a;
+ switch (ip->type)
+ {
+ case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
+ case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support
+ #endif
+ }
+ else if (F.altForm == 2)
+ {
+ #ifdef AF_INET
+ const struct sockaddr *sa;
+ unsigned char *port;
+ sa = (const struct sockaddr*)a;
+ switch (sa->sa_family)
+ {
+ case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr;
+ port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port;
+ DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break;
+ #ifdef AF_INET6
+ case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr;
+ pre[0] = '['; pre[1] = '\0';
+ port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port;
+ DebugSNPrintF(post, sizeof(post), "%%%d]:%d",
+ (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id,
+ (port[0] << 8) | port[1]); break;
+ #endif
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // socket interfaces not included so no sockaddr support
+ #endif
+ }
+ switch (F.precision)
+ {
+ case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s",
+ a[0], a[1], a[2], a[3], post); break;
+ case 6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5]); break;
+ case 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break;
+ case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB),
+ "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s",
+ pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
+ a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break;
+ default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
+ "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break;
+ }
+ }
+ }
+ break;
+
+ case 'U' : {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]),
+ a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break;
+ }
+ }
+ break;
+
+ case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
+
+ case 'C' : if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ i = 4;
+ break;
+
+ case 's' : s = va_arg(arg, char *);
+ if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else switch (F.altForm)
+ {
+ case 0: i=0;
+ if (F.havePrecision) // C string
+ {
+ while((i < F.precision) && s[i]) i++;
+ // Make sure we don't truncate in the middle of a UTF-8 character.
+ // If the last character is part of a multi-byte UTF-8 character, back up to the start of it.
+ j=0;
+ while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; }
+ // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back.
+ if((j > 1) && (j <= 6))
+ {
+ int test = (0xFF << (8-j)) & 0xFF;
+ int mask = test | (1 << ((8-j)-1));
+ if((c & mask) == test) i += j;
+ }
+ }
+ else
+ while(s[i]) i++;
+ break;
+ case 1: i = (unsigned char) *s++; break; // Pascal string
+ case 2: { // DNS label-sequence name
+ unsigned char *a = (unsigned char *)s;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (*a == 0) *s++ = '.'; // Special case for root DNS name
+ while (*a)
+ {
+ if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+ if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+ s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a);
+ a += 1 + *a;
+ }
+ i = (size_t)(s - mDNS_VACB);
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ break;
+
+ case 'S': { // UTF-16 string
+ unsigned char *a = va_arg(arg, unsigned char *);
+ uint16_t *u = (uint16_t*)a;
+ if (!u) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ if ((!F.havePrecision || F.precision))
+ {
+ if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian
+ else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian
+ }
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ switch (F.altForm)
+ {
+ case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian
+ { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; }
+ break;
+ case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian
+ { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian
+ { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ }
+ }
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+
+ #if TARGET_OS_MAC
+ case '@': { // Cocoa/CoreFoundation object
+ CFTypeRef cfObj;
+ CFStringRef cfStr;
+ cfObj = (CFTypeRef) va_arg(arg, void *);
+ cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (cfStr)
+ {
+ CFRange range;
+ CFIndex m;
+ range = CFRangeMake(0, CFStringGetLength(cfStr));
+ m = 0;
+ CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m);
+ CFRelease(cfStr);
+ i = (size_t) m;
+ }
+ else
+ {
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: <invalid CF object>" );
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ break;
+ #endif
+
+ case 'm' : { // Error Message
+ long err;
+ if (F.lSize) err = va_arg(arg, long);
+ else err = va_arg(arg, int);
+ if (F.hSize) err = (short)err;
+ DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB));
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0;s[i];i++) {}
+ }
+ break;
+
+ case 'H' : { // Hex Dump
+ void *a = va_arg(arg, void *);
+ size_t size = (size_t)va_arg(arg, int);
+ size_t max = (size_t)va_arg(arg, int);
+ DebugFlags flags =
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount;
+ if (F.altForm == 0) flags |= kDebugFlagsNoASCII;
+ size = (max < size) ? max : size;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB));
+ }
+ break;
+
+ case 'v' : { // Version
+ uint32_t version;
+ version = va_arg(arg, unsigned int);
+ DebugNumVersionToString(version, mDNS_VACB);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0;s[i];i++) {}
+ }
+ break;
+
+ case 'n' : s = va_arg(arg, char *);
+ if (F.hSize) * (short *) s = (short)nwritten;
+ else if (F.lSize) * (long *) s = (long)nwritten;
+ else * (int *) s = (int)nwritten;
+ continue;
+
+ default: s = mDNS_VACB;
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+ case '%' : *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ break;
+ }
+
+ if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
+ do {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ } while (i < --F.fieldWidth);
+
+ if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ nwritten += i;
+ if (nwritten >= buflen) goto exit;
+
+ for (; i < F.fieldWidth; i++) // Pad on the right
+ {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ }
+ }
+ }
+ exit:
+ *sbuffer++ = 0;
+ return(nwritten);
+ }
+
+//===========================================================================================================================
+// DebugGetErrorString
+//===========================================================================================================================
+
+DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize )
+{
+ const char * s;
+ char * dst;
+ char * end;
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ char buffer[ 256 ];
+#endif
+
+ switch( inErrorCode )
+ {
+ #define CaseErrorString( X, STR ) case X: s = STR; break
+ #define CaseErrorStringify( X ) case X: s = # X; break
+ #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break
+
+ // General Errors
+
+ CaseErrorString( 0, "no error" );
+ CaseErrorString( 1, "in-progress/waiting" );
+ CaseErrorString( -1, "catch-all unknown error" );
+
+ // ACP Errors
+
+ CaseErrorStringifyHardCode( -2, kACPBadRequestErr );
+ CaseErrorStringifyHardCode( -3, kACPNoMemoryErr );
+ CaseErrorStringifyHardCode( -4, kACPBadParamErr );
+ CaseErrorStringifyHardCode( -5, kACPNotFoundErr );
+ CaseErrorStringifyHardCode( -6, kACPBadChecksumErr );
+ CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr );
+ CaseErrorStringifyHardCode( -8, kACPNetworkErr );
+ CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr );
+ CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr );
+ CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr );
+ CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr );
+ CaseErrorStringifyHardCode( -13, kACPNoResourcesErr );
+ CaseErrorStringifyHardCode( -14, kACPBadOptionErr );
+ CaseErrorStringifyHardCode( -15, kACPBadSizeErr );
+ CaseErrorStringifyHardCode( -16, kACPBadPasswordErr );
+ CaseErrorStringifyHardCode( -17, kACPNotInitializedErr );
+ CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr );
+ CaseErrorStringifyHardCode( -19, kACPBadVersionErr );
+ CaseErrorStringifyHardCode( -20, kACPBadSignatureErr );
+ CaseErrorStringifyHardCode( -21, kACPBadIndexErr );
+ CaseErrorStringifyHardCode( -22, kACPUnsupportedErr );
+ CaseErrorStringifyHardCode( -23, kACPInUseErr );
+ CaseErrorStringifyHardCode( -24, kACPParamCountErr );
+ CaseErrorStringifyHardCode( -25, kACPIDErr );
+ CaseErrorStringifyHardCode( -26, kACPFormatErr );
+ CaseErrorStringifyHardCode( -27, kACPUnknownUserErr );
+ CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr );
+ CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr );
+
+ // Common Services Errors
+
+ CaseErrorStringify( kUnknownErr );
+ CaseErrorStringify( kOptionErr );
+ CaseErrorStringify( kSelectorErr );
+ CaseErrorStringify( kExecutionStateErr );
+ CaseErrorStringify( kPathErr );
+ CaseErrorStringify( kParamErr );
+ CaseErrorStringify( kParamCountErr );
+ CaseErrorStringify( kCommandErr );
+ CaseErrorStringify( kIDErr );
+ CaseErrorStringify( kStateErr );
+ CaseErrorStringify( kRangeErr );
+ CaseErrorStringify( kRequestErr );
+ CaseErrorStringify( kResponseErr );
+ CaseErrorStringify( kChecksumErr );
+ CaseErrorStringify( kNotHandledErr );
+ CaseErrorStringify( kVersionErr );
+ CaseErrorStringify( kSignatureErr );
+ CaseErrorStringify( kFormatErr );
+ CaseErrorStringify( kNotInitializedErr );
+ CaseErrorStringify( kAlreadyInitializedErr );
+ CaseErrorStringify( kNotInUseErr );
+ CaseErrorStringify( kInUseErr );
+ CaseErrorStringify( kTimeoutErr );
+ CaseErrorStringify( kCanceledErr );
+ CaseErrorStringify( kAlreadyCanceledErr );
+ CaseErrorStringify( kCannotCancelErr );
+ CaseErrorStringify( kDeletedErr );
+ CaseErrorStringify( kNotFoundErr );
+ CaseErrorStringify( kNoMemoryErr );
+ CaseErrorStringify( kNoResourcesErr );
+ CaseErrorStringify( kDuplicateErr );
+ CaseErrorStringify( kImmutableErr );
+ CaseErrorStringify( kUnsupportedDataErr );
+ CaseErrorStringify( kIntegrityErr );
+ CaseErrorStringify( kIncompatibleErr );
+ CaseErrorStringify( kUnsupportedErr );
+ CaseErrorStringify( kUnexpectedErr );
+ CaseErrorStringify( kValueErr );
+ CaseErrorStringify( kNotReadableErr );
+ CaseErrorStringify( kNotWritableErr );
+ CaseErrorStringify( kBadReferenceErr );
+ CaseErrorStringify( kFlagErr );
+ CaseErrorStringify( kMalformedErr );
+ CaseErrorStringify( kSizeErr );
+ CaseErrorStringify( kNameErr );
+ CaseErrorStringify( kNotReadyErr );
+ CaseErrorStringify( kReadErr );
+ CaseErrorStringify( kWriteErr );
+ CaseErrorStringify( kMismatchErr );
+ CaseErrorStringify( kDateErr );
+ CaseErrorStringify( kUnderrunErr );
+ CaseErrorStringify( kOverrunErr );
+ CaseErrorStringify( kEndingErr );
+ CaseErrorStringify( kConnectionErr );
+ CaseErrorStringify( kAuthenticationErr );
+ CaseErrorStringify( kOpenErr );
+ CaseErrorStringify( kTypeErr );
+ CaseErrorStringify( kSkipErr );
+ CaseErrorStringify( kNoAckErr );
+ CaseErrorStringify( kCollisionErr );
+ CaseErrorStringify( kBackoffErr );
+ CaseErrorStringify( kNoAddressAckErr );
+ CaseErrorStringify( kBusyErr );
+ CaseErrorStringify( kNoSpaceErr );
+
+ // mDNS/DNS-SD Errors
+
+ CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr );
+ CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr );
+ CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr );
+ CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr );
+ CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr );
+ CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr );
+ CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr );
+ CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr );
+ CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr );
+ CaseErrorStringifyHardCode( -65546, mStatus_NoCache );
+ CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered );
+ CaseErrorStringifyHardCode( -65548, mStatus_NameConflict );
+ CaseErrorStringifyHardCode( -65549, mStatus_Invalid );
+ CaseErrorStringifyHardCode( -65550, mStatus_GrowCache );
+ CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr );
+ CaseErrorStringifyHardCode( -65552, mStatus_Incompatible );
+ CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged );
+ CaseErrorStringifyHardCode( -65792, mStatus_MemFree );
+
+ // RSP Errors
+
+ CaseErrorStringifyHardCode( -400000, kRSPUnknownErr );
+ CaseErrorStringifyHardCode( -400050, kRSPParamErr );
+ CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr );
+ CaseErrorStringifyHardCode( -405246, kRSPRangeErr );
+ CaseErrorStringifyHardCode( -409057, kRSPSizeErr );
+ CaseErrorStringifyHardCode( -400200, kRSPHardwareErr );
+ CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr );
+ CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr );
+ CaseErrorStringifyHardCode( -402419, kRSPIDErr );
+ CaseErrorStringifyHardCode( -403165, kRSPFlagErr );
+ CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" );
+ CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" );
+ CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" );
+ CaseErrorString( -200051, "kRSPChecksumErr - 0x33" );
+ CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" );
+ CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" );
+ CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" );
+
+ // XML Errors
+
+ CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr );
+ CaseErrorStringifyHardCode( -100050, kXMLParamErr );
+ CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr );
+ CaseErrorStringifyHardCode( -100206, kXMLFormatErr );
+ CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr );
+ CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr );
+ CaseErrorStringifyHardCode( -101726, kXMLKeyErr );
+ CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr );
+ CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr );
+ CaseErrorStringifyHardCode( -103026, kXMLParseErr );
+ CaseErrorStringifyHardCode( -103159, kXMLBadDataErr );
+ CaseErrorStringifyHardCode( -103170, kXMLBadNameErr );
+ CaseErrorStringifyHardCode( -105246, kXMLRangeErr );
+ CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr );
+ CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr );
+ CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr );
+ CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr );
+ CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr );
+ CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr );
+ CaseErrorStringifyHardCode( -102015, kXMLDateErr );
+
+ #if( __MACH__ )
+
+ // Mach Errors
+
+ CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE );
+ CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE );
+ CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL );
+ CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL );
+ CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST );
+ CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL );
+ CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY );
+ CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT );
+ CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY );
+ CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER );
+ CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER );
+ CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE );
+ CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME );
+ CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED );
+ CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED );
+ CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET );
+ CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL );
+ CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED );
+
+ // Mach OSReturn Errors
+
+ CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError );
+ CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal );
+ CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances );
+ CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit );
+ CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData );
+ CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts );
+ CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet );
+ CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet );
+ CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper );
+ CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper );
+ CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass );
+
+ // IOKit Errors
+
+ CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError );
+ CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory );
+ CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources );
+ CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError );
+ CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice );
+ CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged );
+ CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument );
+ CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead );
+ CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite );
+ CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess );
+ CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID );
+ CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported );
+ CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError );
+ CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError );
+ CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError );
+ CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock );
+ CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen );
+ CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable );
+ CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable );
+ CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned );
+ CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia );
+ CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen );
+ CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError );
+ CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError );
+ CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy );
+ CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout );
+ CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline );
+ CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady );
+ CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached );
+ CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels );
+ CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace );
+ CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists );
+ CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire );
+ CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt );
+ CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames );
+ CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge );
+ CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted );
+ CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower );
+ CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia );
+ CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia );
+ CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode );
+ CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun );
+ CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun );
+ CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError );
+ CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion );
+ CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted );
+ CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth );
+ CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding );
+ CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld );
+ CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew );
+ CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound );
+ CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid );
+
+ // IOKit FireWire Errors
+
+ CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase );
+ CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset );
+ CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry );
+ CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending );
+ CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken );
+ CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid );
+ CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered );
+ CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers );
+ CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive );
+ CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker );
+ CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels );
+ CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable );
+ CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus );
+ CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs );
+ CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage );
+ CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower );
+ CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels );
+ CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram );
+ CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening );
+ CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept );
+ CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose );
+ CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged );
+ CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged );
+
+ // IOKit USB Errors
+
+ CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr );
+ CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr );
+ CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr );
+ CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound );
+ CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound );
+ CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout );
+ CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned );
+ CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled );
+ CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound );
+ CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError );
+ CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr );
+ CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err );
+ CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err );
+ CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr );
+ CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr );
+ CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err );
+ CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err );
+ CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr );
+ CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr );
+ CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr );
+ CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr );
+ CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr );
+
+ #endif // __MACH__
+
+ // Other Errors
+
+ default:
+ s = NULL;
+ #if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ DWORD n;
+
+ n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode,
+ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL );
+ if( n > 0 )
+ {
+ // Remove any trailing CR's or LF's since some messages have them.
+
+ while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) )
+ {
+ buffer[ --n ] = '\0';
+ }
+ s = buffer;
+ }
+ }
+ #endif
+
+ if( !s )
+ {
+ #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+ s = strerror( inErrorCode );
+ #endif
+ if( !s )
+ {
+ s = "<unknown error code>";
+ }
+ }
+ break;
+ }
+
+ // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string.
+
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ dst = inBuffer;
+ end = dst + ( inBufferSize - 1 );
+ while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) )
+ {
+ *dst++ = *s++;
+ }
+ *dst = '\0';
+ s = inBuffer;
+ }
+ return( s );
+}
+
+//===========================================================================================================================
+// DebugHexDump
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t
+ DebugHexDump(
+ DebugLevel inLevel,
+ int inIndent,
+ const char * inLabel,
+ size_t inLabelSize,
+ int inLabelMinWidth,
+ const char * inType,
+ size_t inTypeSize,
+ const void * inDataStart,
+ const void * inData,
+ size_t inDataSize,
+ DebugFlags inFlags,
+ char * outBuffer,
+ size_t inBufferSize )
+{
+ static const char kHexChars[] = "0123456789ABCDEF";
+ const uint8_t * start;
+ const uint8_t * src;
+ char * dst;
+ char * end;
+ size_t n;
+ int offset;
+ int width;
+ const char * newline;
+ char separator[ 8 ];
+ char * s;
+
+ DEBUG_UNUSED( inType );
+ DEBUG_UNUSED( inTypeSize );
+
+ // Set up the function-wide variables.
+
+ if( inLabelSize == kSizeCString )
+ {
+ inLabelSize = strlen( inLabel );
+ }
+ start = (const uint8_t *) inData;
+ src = start;
+ dst = outBuffer;
+ end = dst + inBufferSize;
+ offset = (int)( (intptr_t) inData - (intptr_t) inDataStart );
+ width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth;
+ newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n";
+
+ // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines.
+
+ s = separator;
+ if( inFlags & kDebugFlagsNoNewLine )
+ {
+ if( inFlags & kDebugFlags8BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( inFlags & kDebugFlags16BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) )
+ {
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - separator ) ) < sizeof( separator ) );
+ }
+ *s = '\0';
+
+ for( ;; )
+ {
+ char prefixString[ 32 ];
+ char hexString[ 64 ];
+ char asciiString[ 32 ];
+ char byteCountString[ 32 ];
+ int c;
+ size_t chunkSize;
+ size_t i;
+
+ // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit.
+
+ if( inDataSize == 0 )
+ {
+ if( inLabel && ( inLabelSize > 0 ) )
+ {
+ width = 0;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ width += 8; // "00000000"
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 1; // "+"
+ }
+ }
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ width += 8; // "00000000"
+ }
+ else if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 4; // "0000"
+ }
+
+ if( outBuffer )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ }
+ break;
+ }
+
+ // Build the prefix string. It will be in one of the following formats:
+ //
+ // 1) "00000000+0000[0000]" (address and offset)
+ // 2) "00000000" (address only)
+ // 3) "0000[0000]" (offset only)
+ // 4) "" (no address or offset)
+ //
+ // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate.
+
+ s = prefixString;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ];
+ *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ];
+
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ *s++ = '+';
+ }
+ }
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ *s++ = kHexChars[ ( offset >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 16 ) & 0xF ];
+ }
+ *s++ = kHexChars[ ( offset >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 4 ) & 0xF ];
+ *s++ = kHexChars[ offset & 0xF ];
+ }
+ if( s != prefixString )
+ {
+ *s++ = ':';
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) );
+ *s = '\0';
+
+ // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read.
+ // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up).
+
+ s = hexString;
+ chunkSize = ( inDataSize < 16 ) ? inDataSize : 16;
+ n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16;
+ for( i = 0; i < n; ++i )
+ {
+ if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( i < chunkSize )
+ {
+ *s++ = kHexChars[ src[ i ] >> 4 ];
+ *s++ = kHexChars[ src[ i ] & 0xF ];
+ }
+ else
+ {
+ *s++ = ' ';
+ *s++ = ' ';
+ }
+ }
+ check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) );
+ *s = '\0';
+
+ // Build a string with the ASCII version of the data (replaces non-printable characters with '^').
+ // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up).
+
+ s = asciiString;
+ if( !( inFlags & kDebugFlagsNoASCII ) )
+ {
+ *s++ = ' ';
+ *s++ = '|';
+ for( i = 0; i < n; ++i )
+ {
+ if( i < chunkSize )
+ {
+ c = src[ i ];
+ if( !DebugIsPrint( c ) )
+ {
+ c = '^';
+ }
+ }
+ else
+ {
+ c = '`';
+ }
+ *s++ = (char) c;
+ }
+ *s++ = '|';
+ check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) );
+ }
+ *s = '\0';
+
+ // Build a string indicating how bytes are in the hex dump. Only printed on the first line.
+
+ s = byteCountString;
+ if( !( inFlags & kDebugFlagsNoByteCount ) )
+ {
+ if( src == start )
+ {
+ s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize );
+ }
+ }
+ check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) );
+ *s = '\0';
+
+ // Build the entire line from all the pieces we've previously built.
+
+ if( outBuffer )
+ {
+ if( src == start )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel ? inLabel : "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+ else
+ {
+ if( src == start )
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel,
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+
+ // Move to the next chunk. Exit if there is no more data.
+
+ offset += (int) chunkSize;
+ src += chunkSize;
+ inDataSize -= chunkSize;
+ if( inDataSize == 0 )
+ {
+ break;
+ }
+ }
+
+ // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative.
+
+ return( (size_t)( dst - outBuffer ) );
+}
+
+//===========================================================================================================================
+// DebugNumVersionToString
+//===========================================================================================================================
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString )
+{
+ char * s;
+ uint8_t majorRev;
+ uint8_t minor;
+ uint8_t bugFix;
+ uint8_t stage;
+ uint8_t revision;
+
+ check( inString );
+
+ majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF );
+ minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F );
+ bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F );
+ stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF );
+ revision = (uint8_t)( inVersion & 0xFF );
+
+ // Convert the major, minor, and bugfix numbers.
+
+ s = inString;
+ s += sprintf( s, "%u", majorRev );
+ s += sprintf( s, ".%u", minor );
+ if( bugFix != 0 )
+ {
+ s += sprintf( s, ".%u", bugFix );
+ }
+
+ // Convert the version stage and non-release revision number.
+
+ switch( stage )
+ {
+ case kVersionStageDevelopment:
+ s += sprintf( s, "d%u", revision );
+ break;
+
+ case kVersionStageAlpha:
+ s += sprintf( s, "a%u", revision );
+ break;
+
+ case kVersionStageBeta:
+ s += sprintf( s, "b%u", revision );
+ break;
+
+ case kVersionStageFinal:
+
+ // A non-release revision of zero is a special case indicating the software is GM (at the golden master
+ // stage) and therefore, the non-release revision should not be added to the string.
+
+ if( revision != 0 )
+ {
+ s += sprintf( s, "f%u", revision );
+ }
+ break;
+
+ default:
+ dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage );
+ break;
+ }
+ return( inString );
+}
+
+//===========================================================================================================================
+// DebugTaskLevel
+//===========================================================================================================================
+
+DEBUG_EXPORT uint32_t DebugTaskLevel( void )
+{
+ uint32_t level;
+
+ level = 0;
+
+#if( TARGET_OS_VXWORKS )
+ if( intContext() )
+ {
+ level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask );
+ }
+#endif
+
+ return( level );
+}
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWinEnableConsole
+//===========================================================================================================================
+
+#pragma warning( disable:4311 )
+
+static void DebugWinEnableConsole( void )
+{
+ static bool sConsoleEnabled = false;
+ BOOL result;
+ int fileHandle;
+ FILE * file;
+ int err;
+
+ if( sConsoleEnabled )
+ {
+ goto exit;
+ }
+
+ // Create console window.
+
+ result = AllocConsole();
+ require_quiet( result, exit );
+
+ // Redirect stdin to the console stdin.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "r", stdin );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "r" );
+ require_quiet( file, exit );
+
+ *stdin = *file;
+ #endif
+
+ err = setvbuf( stdin, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stdout to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stdout );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stdout = *file;
+ #endif
+
+ err = setvbuf( stdout, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stderr to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stderr );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stderr = *file;
+ #endif
+
+ err = setvbuf( stderr, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ sConsoleEnabled = true;
+
+exit:
+ return;
+}
+
+#pragma warning( default:4311 )
+
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWinCharToTCharString
+//===========================================================================================================================
+
+static TCHAR *
+ DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount )
+{
+ const char * src;
+ TCHAR * dst;
+ TCHAR * end;
+
+ if( inCharCount == kSizeCString )
+ {
+ inCharCount = strlen( inCharString );
+ }
+ src = inCharString;
+ dst = outTCharString;
+ if( inTCharCountMax > 0 )
+ {
+ inTCharCountMax -= 1;
+ if( inTCharCountMax > inCharCount )
+ {
+ inTCharCountMax = inCharCount;
+ }
+
+ end = dst + inTCharCountMax;
+ while( dst < end )
+ {
+ *dst++ = (TCHAR) *src++;
+ }
+ *dst = 0;
+ }
+ if( outTCharCount )
+ {
+ *outTCharCount = (size_t)( dst - outTCharString );
+ }
+ return( outTCharString );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Debugging ==
+#endif
+
+//===========================================================================================================================
+// DebugServicesTest
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugServicesTest( void )
+{
+ OSStatus err;
+ char s[ 512 ];
+ uint8_t * p;
+ uint8_t data[] =
+ {
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0xAA,
+ 0xBB, 0xCC, 0xDD,
+ 0xEE,
+ 0xFF,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0,
+ 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1
+ };
+
+ debug_initialize( kDebugOutputTypeMetaConsole );
+
+ // check's
+
+ check( 0 && "SHOULD SEE: check" );
+ check( 1 && "SHOULD *NOT* SEE: check (valid)" );
+ check_string( 0, "SHOULD SEE: check_string" );
+ check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" );
+ check_noerr( -123 );
+ check_noerr( 10038 );
+ check_noerr( 22 );
+ check_noerr( 0 );
+ check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" );
+ check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" );
+ check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 );
+
+ // require's
+
+ require( 0 && "SHOULD SEE", require1 );
+ { err = kResponseErr; goto exit; }
+require1:
+ require( 1 && "SHOULD *NOT* SEE", require2 );
+ goto require2Good;
+require2:
+ { err = kResponseErr; goto exit; }
+require2Good:
+ require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" );
+ { err = kResponseErr; goto exit; }
+require3:
+ require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" );
+ goto require4Good;
+require4:
+ { err = kResponseErr; goto exit; }
+require4Good:
+ require_quiet( 0 && "SHOULD SEE", require5 );
+ { err = kResponseErr; goto exit; }
+require5:
+ require_quiet( 1 && "SHOULD *NOT* SEE", require6 );
+ goto require6Good;
+require6:
+ { err = kResponseErr; goto exit; }
+require6Good:
+ require_noerr( -1, require7 );
+ { err = kResponseErr; goto exit; }
+require7:
+ require_noerr( 0, require8 );
+ goto require8Good;
+require8:
+ { err = kResponseErr; goto exit; }
+require8Good:
+ require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string");
+ { err = kResponseErr; goto exit; }
+require9:
+ require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" );
+ goto require10Good;
+require10:
+ { err = kResponseErr; goto exit; }
+require10Good:
+ require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" );
+ { err = kResponseErr; goto exit; }
+require11:
+ require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" );
+ goto require12Good;
+require12:
+ { err = kResponseErr; goto exit; }
+require12Good:
+ require_noerr_quiet( -4, require13 );
+ { err = kResponseErr; goto exit; }
+require13:
+ require_noerr_quiet( 0, require14 );
+ goto require14Good;
+require14:
+ { err = kResponseErr; goto exit; }
+require14Good:
+ require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require15:
+ require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) );
+ goto require16Good;
+require16:
+ { err = kResponseErr; goto exit; }
+require16Good:
+ require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require17:
+ require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) );
+ goto require18Good;
+require18:
+ { err = kResponseErr; goto exit; }
+require18Good:
+ require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require19:
+ require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) );
+ goto require20Good;
+require20:
+ { err = kResponseErr; goto exit; }
+require20Good:
+ require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require21:
+ require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) );
+ goto require22Good;
+require22:
+ { err = kResponseErr; goto exit; }
+require22Good:
+ require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" );
+ { err = kResponseErr; goto exit; }
+require23:
+ require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" );
+ goto require24Good;
+require24:
+ { err = kResponseErr; goto exit; }
+require24Good:
+
+#if( defined( __MWERKS__ ) )
+ #if( defined( __cplusplus ) && __option( exceptions ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#else
+ #if( defined( __cplusplus ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#endif
+
+#if( COMPILER_HAS_EXCEPTIONS )
+ try
+ {
+ require_throw( 1 && "SHOULD *NOT* SEE" );
+ require_throw( 0 && "SHOULD SEE" );
+ }
+ catch( ... )
+ {
+ goto require26Good;
+ }
+ { err = kResponseErr; goto exit; }
+require26Good:
+#endif
+
+ // translate_errno
+
+ err = translate_errno( 1 != -1, -123, -567 );
+ require( ( err == 0 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, -123, -567 );
+ require( ( err == -123 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, 0, -567 );
+ require( ( err == -567 ) && "SHOULD *NOT* SEE", exit );
+
+ // debug_string
+
+ debug_string( "debug_string" );
+
+ // DebugSNPrintF
+
+ DebugSNPrintF( s, sizeof( s ), "%d", 1234 );
+ require_action( strcmp( s, "1234" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 );
+ require_action( strcmp( s, "2345" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" );
+ require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) );
+ require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) );
+ require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 );
+
+ #if( TYPE_LONGLONG_NATIVE )
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) );
+ require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) );
+ require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) );
+ require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) );
+ require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd'
+ require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 );
+
+ #if( defined( MDNS_DEBUGMSGS ) )
+ {
+ mDNSAddr maddr;
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv4;
+ maddr.ip.v4.b[ 0 ] = 127;
+ maddr.ip.v4.b[ 1 ] = 0;
+ maddr.ip.v4.b[ 2 ] = 0;
+ maddr.ip.v4.b[ 3 ] = 1;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 );
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv6;
+ maddr.ip.v6.b[ 0 ] = 0xFE;
+ maddr.ip.v6.b[ 1 ] = 0x80;
+ maddr.ip.v6.b[ 15 ] = 0x01;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if( AF_INET )
+ {
+ struct sockaddr_in sa4;
+
+ memset( &sa4, 0, sizeof( sa4 ) );
+ sa4.sin_family = AF_INET;
+ p = (uint8_t *) &sa4.sin_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ p = (uint8_t *) &sa4.sin_addr.s_addr;
+ p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF );
+ p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF );
+ p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF );
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 );
+ require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if( AF_INET6 )
+ {
+ struct sockaddr_in6 sa6;
+
+ memset( &sa6, 0, sizeof( sa6 ) );
+ sa6.sin6_family = AF_INET6;
+ p = (uint8_t *) &sa6.sin6_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ sa6.sin6_addr.s6_addr[ 0 ] = 0xFE;
+ sa6.sin6_addr.s6_addr[ 1 ] = 0x80;
+ sa6.sin6_addr.s6_addr[ 15 ] = 0x01;
+ sa6.sin6_scope_id = 2;
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 );
+ require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ // Unicode
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ #if( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ #if( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ // Misc
+
+ DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" );
+ require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%m", 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"",
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8",
+ 32, 32 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ // Hex Dumps
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd'
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount,
+ s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine |
+ kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ // dlog's
+
+ dlog( kDebugLevelNotice, "dlog\n" );
+ dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 );
+ dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" );
+ dlogmem( kDebugLevelNotice, data, sizeof( data ) );
+
+ // Done
+
+ DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" );
+ err = kNoErr;
+
+exit:
+ if( err )
+ {
+ DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" );
+ }
+ return( err );
+}
+
+#endif // DEBUG
diff --git a/src/tools/mdnssd/EventLog.mc b/src/tools/mdnssd/EventLog.mc
new file mode 100644
index 0000000000..248e6c1a18
--- /dev/null
+++ b/src/tools/mdnssd/EventLog.mc
@@ -0,0 +1,11 @@
+MessageIdTypedef=WORD
+LanguageNames=(English=0x409:MSG00409)
+
+MessageId=100
+SymbolicName=MDNSRESPONDER_LOG
+Severity=Success
+Facility=Application
+Language=English
+%1
+.
+
diff --git a/src/tools/mdnssd/Firewall.cpp b/src/tools/mdnssd/Firewall.cpp
new file mode 100644
index 0000000000..c7c96d09f8
--- /dev/null
+++ b/src/tools/mdnssd/Firewall.cpp
@@ -0,0 +1,484 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// <rdar://problem/4278931> Doesn't compile correctly with latest Platform SDK
+
+#if !defined(_WIN32_DCOM)
+# define _WIN32_DCOM
+#endif
+
+
+#include "Firewall.h"
+#include <windows.h>
+#include <crtdbg.h>
+#include <netfw.h>
+#include <objbase.h>
+#include <oleauto.h>
+
+
+static const int kMaxTries = 30;
+static const int kRetrySleepPeriod = 1 * 1000; // 1 second
+
+
+static OSStatus
+mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile)
+{
+ INetFwMgr * fwMgr = NULL;
+ INetFwPolicy * fwPolicy = NULL;
+ int numRetries = 0;
+ HRESULT err = kNoErr;
+
+ _ASSERT(fwProfile != NULL);
+
+ *fwProfile = NULL;
+
+ // Use COM to get a reference to the firewall settings manager. This
+ // call will fail on anything other than XP SP2
+
+ err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr );
+ require(SUCCEEDED(err) && ( fwMgr != NULL ), exit);
+
+ // Use the reference to get the local firewall policy
+
+ err = fwMgr->get_LocalPolicy(&fwPolicy);
+ require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit);
+
+ // Use the reference to get the extant profile. Empirical evidence
+ // suggests that there is the potential for a race condition when a system
+ // service whose startup type is automatic calls this method.
+ // This is true even when the service declares itself to be dependent
+ // on the firewall service. Re-trying the method will succeed within
+ // a few seconds.
+
+ do
+ {
+ err = fwPolicy->get_CurrentProfile(fwProfile);
+
+ if (err)
+ {
+ Sleep(kRetrySleepPeriod);
+ }
+ }
+ while (err && (numRetries++ < kMaxTries));
+
+ require(SUCCEEDED(err), exit);
+
+ err = kNoErr;
+
+exit:
+
+ // Release temporary COM objects
+
+ if (fwPolicy != NULL)
+ {
+ fwPolicy->Release();
+ }
+
+ if (fwMgr != NULL)
+ {
+ fwMgr->Release();
+ }
+
+ return err;
+}
+
+
+static void
+mDNSFirewallCleanup
+ (
+ IN INetFwProfile * fwProfile
+ )
+{
+ // Call Release on the COM reference.
+
+ if (fwProfile != NULL)
+ {
+ fwProfile->Release();
+ }
+}
+
+
+static OSStatus
+mDNSFirewallAppIsEnabled
+ (
+ IN INetFwProfile * fwProfile,
+ IN const wchar_t * fwProcessImageFileName,
+ OUT BOOL * fwAppEnabled
+ )
+{
+ BSTR fwBstrProcessImageFileName = NULL;
+ VARIANT_BOOL fwEnabled;
+ INetFwAuthorizedApplication * fwApp = NULL;
+ INetFwAuthorizedApplications* fwApps = NULL;
+ OSStatus err = kNoErr;
+
+ _ASSERT(fwProfile != NULL);
+ _ASSERT(fwProcessImageFileName != NULL);
+ _ASSERT(fwAppEnabled != NULL);
+
+ *fwAppEnabled = FALSE;
+
+ // Get the list of authorized applications
+
+ err = fwProfile->get_AuthorizedApplications(&fwApps);
+ require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
+
+ fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
+ require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
+
+ // Look for us
+
+ err = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
+
+ if (SUCCEEDED(err) && ( fwApp != NULL ) )
+ {
+ // It's listed, but is it enabled?
+
+ err = fwApp->get_Enabled(&fwEnabled);
+ require(SUCCEEDED(err), exit);
+
+ if (fwEnabled != VARIANT_FALSE)
+ {
+ // Yes, it's enabled
+
+ *fwAppEnabled = TRUE;
+ }
+ }
+
+ err = kNoErr;
+
+exit:
+
+ // Deallocate the BSTR
+
+ if ( fwBstrProcessImageFileName != NULL )
+ {
+ SysFreeString(fwBstrProcessImageFileName);
+ }
+
+ // Release the COM objects
+
+ if (fwApp != NULL)
+ {
+ fwApp->Release();
+ }
+
+ if (fwApps != NULL)
+ {
+ fwApps->Release();
+ }
+
+ return err;
+}
+
+
+static OSStatus
+mDNSFirewallAddApp
+ (
+ IN INetFwProfile * fwProfile,
+ IN const wchar_t * fwProcessImageFileName,
+ IN const wchar_t * fwName
+ )
+{
+ BOOL fwAppEnabled;
+ BSTR fwBstrName = NULL;
+ BSTR fwBstrProcessImageFileName = NULL;
+ INetFwAuthorizedApplication * fwApp = NULL;
+ INetFwAuthorizedApplications* fwApps = NULL;
+ OSStatus err = S_OK;
+
+ _ASSERT(fwProfile != NULL);
+ _ASSERT(fwProcessImageFileName != NULL);
+ _ASSERT(fwName != NULL);
+
+ // First check to see if the application is already authorized.
+ err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled );
+ require_noerr(err, exit);
+
+ // Only add the application if it isn't enabled
+
+ if (!fwAppEnabled)
+ {
+ // Get the list of authorized applications
+
+ err = fwProfile->get_AuthorizedApplications(&fwApps);
+ require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
+
+ // Create an instance of an authorized application.
+
+ err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp );
+ require(SUCCEEDED(err) && ( fwApp != NULL ), exit);
+
+ fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
+ require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
+
+ // Set the executable file name
+
+ err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
+ require(SUCCEEDED(err), exit);
+
+ fwBstrName = SysAllocString(fwName);
+ require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr);
+
+ // Set the friendly name
+
+ err = fwApp->put_Name(fwBstrName);
+ require(SUCCEEDED(err), exit);
+
+ // Now add the application
+
+ err = fwApps->Add(fwApp);
+ require(SUCCEEDED(err), exit);
+ }
+
+ err = kNoErr;
+
+exit:
+
+ // Deallocate the BSTR objects
+
+ if ( fwBstrName != NULL )
+ {
+ SysFreeString(fwBstrName);
+ }
+
+ if ( fwBstrProcessImageFileName != NULL )
+ {
+ SysFreeString(fwBstrProcessImageFileName);
+ }
+
+ // Release the COM objects
+
+ if (fwApp != NULL)
+ {
+ fwApp->Release();
+ }
+
+ if (fwApps != NULL)
+ {
+ fwApps->Release();
+ }
+
+ return err;
+}
+
+
+
+
+
+static OSStatus
+
+mDNSFirewallIsFileAndPrintSharingEnabled
+
+ (
+
+ IN INetFwProfile * fwProfile,
+
+ OUT BOOL * fwServiceEnabled
+
+ )
+
+{
+
+ VARIANT_BOOL fwEnabled;
+
+ INetFwService* fwService = NULL;
+
+ INetFwServices* fwServices = NULL;
+
+ OSStatus err = S_OK;
+
+
+
+ _ASSERT(fwProfile != NULL);
+
+ _ASSERT(fwServiceEnabled != NULL);
+
+
+
+ *fwServiceEnabled = FALSE;
+
+
+
+ // Retrieve the globally open ports collection.
+
+ err = fwProfile->get_Services(&fwServices);
+
+ require( SUCCEEDED( err ), exit );
+
+
+
+ // Attempt to retrieve the globally open port.
+
+ err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService);
+
+ require( SUCCEEDED( err ), exit );
+
+
+
+ // Find out if the globally open port is enabled.
+
+ err = fwService->get_Enabled(&fwEnabled);
+
+ require( SUCCEEDED( err ), exit );
+
+ if (fwEnabled != VARIANT_FALSE)
+
+ {
+
+ *fwServiceEnabled = TRUE;
+
+ }
+
+
+
+exit:
+
+
+
+ // Release the globally open port.
+
+ if (fwService != NULL)
+
+ {
+
+ fwService->Release();
+
+ }
+
+
+
+ // Release the globally open ports collection.
+
+ if (fwServices != NULL)
+
+ {
+
+ fwServices->Release();
+
+ }
+
+
+
+ return err;
+
+}
+
+
+OSStatus
+mDNSAddToFirewall
+ (
+ LPWSTR executable,
+ LPWSTR name
+ )
+{
+ INetFwProfile * fwProfile = NULL;
+ HRESULT comInit = E_FAIL;
+ OSStatus err = kNoErr;
+
+ // Initialize COM.
+
+ comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
+
+ // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
+ // initialized with a different mode.
+
+ if (comInit != RPC_E_CHANGED_MODE)
+ {
+ err = comInit;
+ require(SUCCEEDED(err), exit);
+ }
+
+ // Connect to the firewall
+
+ err = mDNSFirewallInitialize(&fwProfile);
+ require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
+
+ // Add us to the list of exempt programs
+
+ err = mDNSFirewallAddApp( fwProfile, executable, name );
+ require_noerr(err, exit);
+
+exit:
+
+ // Disconnect from the firewall
+
+ if ( fwProfile != NULL )
+ {
+ mDNSFirewallCleanup(fwProfile);
+ }
+
+ // De-initialize COM
+
+ if (SUCCEEDED(comInit))
+ {
+ CoUninitialize();
+ }
+
+ return err;
+}
+
+
+BOOL
+mDNSIsFileAndPrintSharingEnabled( BOOL * retry )
+{
+ INetFwProfile * fwProfile = NULL;
+ HRESULT comInit = E_FAIL;
+ BOOL enabled = FALSE;
+ OSStatus err = kNoErr;
+
+ // Initialize COM.
+
+ *retry = FALSE;
+ comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
+
+ // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
+ // initialized with a different mode.
+
+ if (comInit != RPC_E_CHANGED_MODE)
+ {
+ *retry = TRUE;
+ err = comInit;
+ require(SUCCEEDED(err), exit);
+ }
+
+ // Connect to the firewall
+
+ err = mDNSFirewallInitialize(&fwProfile);
+ require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
+
+ err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled );
+ require_noerr( err, exit );
+
+exit:
+
+ // Disconnect from the firewall
+
+ if ( fwProfile != NULL )
+ {
+ mDNSFirewallCleanup(fwProfile);
+ }
+
+ // De-initialize COM
+
+ if (SUCCEEDED(comInit))
+ {
+ CoUninitialize();
+ }
+
+ return enabled;
+}
diff --git a/src/tools/mdnssd/Firewall.h b/src/tools/mdnssd/Firewall.h
new file mode 100644
index 0000000000..3d7d532d17
--- /dev/null
+++ b/src/tools/mdnssd/Firewall.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+#ifndef _Firewall_h
+
+#define _Firewall_h
+
+
+
+
+
+#include "CommonServices.h"
+
+#include "DebugServices.h"
+
+
+
+
+
+#if defined(__cplusplus)
+
+extern "C"
+
+{
+
+#endif
+
+
+
+
+
+OSStatus
+
+mDNSAddToFirewall
+
+ (
+
+ LPWSTR executable,
+
+ LPWSTR name
+
+ );
+
+
+BOOL
+mDNSIsFileAndPrintSharingEnabled( BOOL * retry );
+
+
+
+
+
+#if defined(__cplusplus)
+
+}
+
+#endif
+
+
+
+
+
+#endif
+
diff --git a/src/tools/mdnssd/LegacyNATTraversal.c b/src/tools/mdnssd/LegacyNATTraversal.c
new file mode 100644
index 0000000000..115719e033
--- /dev/null
+++ b/src/tools/mdnssd/LegacyNATTraversal.c
@@ -0,0 +1,906 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+
+#include "stdlib.h" // For strtol()
+#include "string.h" // For strlcpy(), For strncpy(), strncasecmp()
+
+#if defined( WIN32 )
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ;
+
+static int
+inet_pton( int family, const char * addr, void * dst )
+ {
+ struct sockaddr_storage ss;
+ int sslen = sizeof( ss );
+
+ ZeroMemory( &ss, sizeof( ss ) );
+ ss.ss_family = (ADDRESS_FAMILY)family;
+
+ if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
+ {
+ if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
+ else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
+ else return 0;
+ }
+ else return 0;
+ }
+#else
+# include <arpa/inet.h> // For inet_pton()
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+#include "uDNS.h" // For natTraversalHandleAddressReply() etc.
+
+// used to format SOAP port mapping arguments
+typedef struct Property_struct
+ {
+ char *name;
+ char *type;
+ char *value;
+ } Property;
+
+// All of the text parsing in this file is intentionally transparent so that we know exactly
+// what's being done to the text, with an eye towards preventing security problems.
+
+// This is an evolving list of useful acronyms to know. Please add to it at will.
+// ST Service Type
+// NT Notification Type
+// USN Unique Service Name
+// UDN Unique Device Name
+// UUID Universally Unique Identifier
+// URN/urn Universal Resource Name
+
+// Forward declaration because of circular reference:
+// SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
+// In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
+mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
+
+#define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries)
+
+// Note that this function assumes src is already NULL terminated
+mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src)
+ {
+ if (src == mDNSNULL) return;
+ if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL)
+ { LogMsg("AllocAndCopy: can't allocate string"); return; }
+ strcpy((char*)*dst, (char*)src);
+ }
+
+// This function does a simple parse of an HTTP URL that may include a hostname, port, and path
+// If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space)
+mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **const path)
+ {
+ // if the data begins with "http://", we assume there is a hostname and possibly a port number
+ if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0)
+ {
+ int i;
+ const mDNSu8 *stop = end;
+ const mDNSu8 *addrPtr = mDNSNULL;
+
+ ptr += 7; //skip over "http://"
+ if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; }
+
+ // find the end of the host:port
+ addrPtr = ptr;
+ for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break;
+
+ // allocate the buffer (len i+1 so we have space to terminate the string)
+ if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL)
+ { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; }
+ strncpy((char*)*addressAndPort, (char*)ptr, i);
+ (*addressAndPort)[i] = '\0';
+
+ // find the port number in the string, by looking backwards for the ':'
+ stop = ptr; // can't go back farther than the original start
+ ptr = addrPtr; // move ptr to the path part
+
+ for (addrPtr--; addrPtr>stop; addrPtr--)
+ {
+ if (*addrPtr == ':')
+ {
+ addrPtr++; // skip over ':'
+ *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted
+ break;
+ }
+ }
+ }
+
+ // ptr should now point to the first character we haven't yet processed
+ // everything that remains is the path
+ if (path && ptr < end)
+ {
+ if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL)
+ { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; }
+ strncpy((char*)*path, (char*)ptr, end - ptr);
+ (*path)[end - ptr] = '\0';
+ }
+
+ return mStatus_NoError;
+ }
+
+enum
+ {
+ HTTPCode_NeedMoreData = -1, // No code found in stream
+ HTTPCode_Other = -2, // Valid code other than those below found in stream
+ HTTPCode_Bad = -3,
+ HTTPCode_200 = 200,
+ HTTPCode_404 = 404,
+ HTTPCode_500 = 500,
+ };
+
+mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end)
+ {
+ const mDNSu8 *ptr = *data;
+ const mDNSu8 *code;
+
+ if (end - ptr < 5) return HTTPCode_NeedMoreData;
+ if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad;
+ ptr += 5;
+ // should we care about the HTTP protocol version?
+
+ // look for first space, which must come before first LF
+ while (ptr && ptr != end)
+ {
+ if (*ptr == '\n') return HTTPCode_Bad;
+ if (*ptr == ' ') break;
+ ptr++;
+ }
+ if (ptr == end) return HTTPCode_NeedMoreData;
+ ptr++;
+
+ if (end - ptr < 3) return HTTPCode_NeedMoreData;
+
+ code = ptr;
+ ptr += 3;
+ while (ptr && ptr != end)
+ {
+ if (*ptr == '\n') break;
+ ptr++;
+ }
+ if (ptr == end) return HTTPCode_NeedMoreData;
+ *data = ++ptr;
+
+ if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200;
+ if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404;
+ if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500;
+
+ LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]);
+ return HTTPCode_Other;
+ }
+
+// This function parses the xml body of the device description response from the router. Basically, we look to
+// make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection),
+// look for the "controlURL" header immediately following, and copy the addressing and URL info we need
+mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo)
+ {
+ mDNS *m = tcpInfo->m;
+ const mDNSu8 *ptr = tcpInfo->Reply;
+ const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
+ const mDNSu8 *stop;
+ mDNSs16 http_result;
+
+ if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need
+
+ http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
+ if (http_result == HTTPCode_404) LNT_ClearState(m);
+ if (http_result != HTTPCode_200)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result);
+ return;
+ }
+
+ // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection.
+ m->UPnPWANPPPConnection = mDNSfalse;
+
+ // find either service we care about
+ while (ptr && ptr < end)
+ {
+ if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
+ ptr++;
+ }
+ if (ptr == end)
+ {
+ ptr = tcpInfo->Reply;
+ while (ptr && ptr < end)
+ {
+ if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0))
+ {
+ m->UPnPWANPPPConnection = mDNStrue;
+ break;
+ }
+ ptr++;
+ }
+ }
+ if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; }
+
+ // find "controlURL", starting from where we left off
+ while (ptr && ptr < end)
+ {
+ if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking
+ ptr++;
+ }
+ if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; }
+ ptr += 11; // skip over "controlURL>"
+ if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer
+
+ // find the end of the controlURL element
+ for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
+
+ // fill in default port
+ m->UPnPSOAPPort = m->UPnPRouterPort;
+
+ // free string pointers and set to NULL
+ if (m->UPnPSOAPAddressString != mDNSNULL)
+ {
+ mDNSPlatformMemFree(m->UPnPSOAPAddressString);
+ m->UPnPSOAPAddressString = mDNSNULL;
+ }
+ if (m->UPnPSOAPURL != mDNSNULL)
+ {
+ mDNSPlatformMemFree(m->UPnPSOAPURL);
+ m->UPnPSOAPURL = mDNSNULL;
+ }
+
+ if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return;
+ // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc"
+
+ if (m->UPnPSOAPAddressString == mDNSNULL)
+ {
+ ptr = tcpInfo->Reply;
+ while (ptr && ptr < end)
+ {
+ if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break;
+ ptr++;
+ }
+
+ if (ptr < end) // found URLBase
+ {
+ LogInfo("handleLNTDeviceDescriptionResponse: found URLBase");
+ ptr += 8; // skip over "URLBase>"
+ // find the end of the URLBase element
+ for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
+ if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError)
+ {
+ LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase");
+ }
+ }
+
+ // if all else fails, use the router address string
+ if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString);
+ }
+ if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL");
+ else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString);
+
+ if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL);
+ if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL");
+ else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL);
+ }
+
+mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo)
+ {
+ mDNS *m = tcpInfo->m;
+ mDNSu16 err = NATErr_None;
+ mDNSv4Addr ExtAddr;
+ const mDNSu8 *ptr = tcpInfo->Reply;
+ const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
+ mDNSu8 *addrend;
+ static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' };
+ // Array NOT including a terminating nul
+
+// LogInfo("handleLNTGetExternalAddressResponse: %s", ptr);
+
+ mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
+ if (http_result == HTTPCode_404) LNT_ClearState(m);
+ if (http_result != HTTPCode_200)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
+ return;
+ }
+
+ while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++;
+ ptr += sizeof(tagname); // Skip over "NewExternalIPAddress"
+ while (ptr < end && *ptr != '>') ptr++;
+ ptr += 1; // Skip over ">"
+
+ // Find the end of the address and terminate the string so inet_pton() can convert it
+ // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place
+ addrend = (mDNSu8*)ptr;
+ while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++;
+ if (addrend >= end) return;
+ *addrend = 0;
+
+ if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", "");
+ LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr);
+ err = NATErr_NetFail;
+ ExtAddr = zerov4Addr;
+ }
+ if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
+
+ natTraversalHandleAddressReply(m, err, ExtAddr);
+ }
+
+mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
+ {
+ mDNS *m = tcpInfo->m;
+ mDNSIPPort extport = zeroIPPort;
+ const mDNSu8 *ptr = tcpInfo->Reply;
+ const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread;
+ NATTraversalInfo *natInfo;
+ mDNSs16 http_result;
+
+ for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; }
+
+ if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
+
+ http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
+ if (http_result == HTTPCode_200)
+ {
+ LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
+ mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries);
+
+ // Make sure to compute extport *before* we zero tcpInfo->retries
+ extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
+ tcpInfo->retries = 0;
+ natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE);
+ }
+ else if (http_result == HTTPCode_500)
+ {
+ while (ptr && ptr != end)
+ {
+ if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) ||
+ (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0))
+ {
+ if (tcpInfo->retries < 100)
+ {
+ tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo);
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries);
+ }
+ else
+ {
+ LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
+ natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
+ }
+ return;
+ }
+ ptr++;
+ }
+ }
+ else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
+ else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code");
+ else if (http_result == HTTPCode_404) LNT_ClearState(m);
+ if (http_result != HTTPCode_200 && http_result != HTTPCode_500)
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
+ }
+
+mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo)
+ {
+ tcpLNTInfo **ptr = &m->tcpInfoUnmapList;
+ while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next;
+ if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory
+ }
+
+mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
+ {
+ mStatus status = mStatus_NoError;
+ tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context;
+ mDNSBool closed = mDNSfalse;
+ long n = 0;
+ long nsent = 0;
+
+ if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; }
+
+ // The handlers below expect to be called with the lock held
+ mDNS_Lock(tcpInfo->m);
+
+ if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; }
+
+ if (ConnectionEstablished) // connection is established - send the message
+ {
+ LogInfo("tcpConnectionCallback: connection established, sending message");
+ nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen);
+ if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; }
+ }
+ else
+ {
+ n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed);
+ LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n);
+
+ if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; }
+ else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; }
+
+ tcpInfo->nread += n;
+ LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread);
+ if (tcpInfo->nread > LNT_MAXBUFSIZE)
+ {
+ LogInfo("result truncated...");
+ tcpInfo->nread = LNT_MAXBUFSIZE;
+ }
+
+ switch (tcpInfo->op)
+ {
+ case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break;
+ case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break;
+ case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break;
+ case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break;
+ default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break;
+ }
+ }
+exit:
+ if (err || status)
+ {
+ mDNS *m = tcpInfo->m;
+ switch (tcpInfo->op)
+ {
+ case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL)
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", "");
+ if (m->UPnPSOAPURL == mDNSNULL)
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", "");
+ if (m->UPnPSOAPAddressString && m->UPnPSOAPURL)
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
+ break;
+ case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest",
+ mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success",
+ mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", "");
+ break;
+ case LNTPortMapOp: if (tcpInfo->parentNATInfo)
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
+ (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result);
+ break;
+ case LNTPortMapDeleteOp: break;
+ default: break;
+ }
+
+ mDNSPlatformTCPCloseConnection(tcpInfo->sock);
+ tcpInfo->sock = mDNSNULL;
+ if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; }
+ if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; }
+ }
+
+ if (tcpInfo) mDNS_Unlock(tcpInfo->m);
+
+ if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo);
+ }
+
+mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
+ {
+ mStatus err = mStatus_NoError;
+ mDNSIPPort srcport = zeroIPPort;
+
+ if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
+ { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
+ info->m = m;
+ info->Address = *Addr;
+ info->Port = Port;
+ info->op = op;
+ info->nread = 0;
+ info->replyLen = LNT_MAXBUFSIZE;
+ if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer
+ else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }
+
+ if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
+ info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport);
+ if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
+ LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
+ err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info);
+
+ if (err == mStatus_ConnPending) err = mStatus_NoError;
+ else if (err == mStatus_ConnEstablished)
+ {
+ mDNS_DropLockBeforeCallback();
+ tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
+ mDNS_ReclaimLockAfterCallback();
+ err = mStatus_NoError;
+ }
+ else
+ {
+ // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
+ LogInfo("LNT MakeTCPConnection: connection failed");
+ mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above
+ info->sock = mDNSNULL;
+ mDNSPlatformMemFree(info->Reply);
+ info->Reply = mDNSNULL;
+ }
+ return(err);
+ }
+
+mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a)
+ {
+ static const char f1[] = "<%s>%s</%s>";
+ static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
+ int i, len = 0;
+ *buf = 0;
+ for (i = 0; i < numArgs; i++)
+ {
+ if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name);
+ else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name);
+ }
+ return(len);
+ }
+
+mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op)
+ {
+ // SOAP message header format -
+ // - control URL
+ // - action (string)
+ // - router's host/port ("host:port")
+ // - content-length
+ static const char header[] =
+ "POST %s HTTP/1.1\r\n"
+ "Content-Type: text/xml; charset=\"utf-8\"\r\n"
+ "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %d\r\n"
+ "Connection: close\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s\r\n";
+
+ static const char body1[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope"
+ " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
+ " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<SOAP-ENV:Body>"
+ "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
+
+ static const char body2[] =
+ "</m:%s>"
+ "</SOAP-ENV:Body>"
+ "</SOAP-ENV:Envelope>\r\n";
+
+ mStatus err;
+ char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
+ int bodyLen;
+
+ if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here
+ { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; }
+
+ // Create body
+ bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP");
+ bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
+ bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action);
+
+ // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
+ if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
+ if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; }
+ info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body);
+
+ err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op);
+ if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; }
+ return err;
+ }
+
+// Build port mapping request with new port (up to max) and send it
+mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
+ {
+ char externalPort[6];
+ char internalPort[6];
+ char localIPAddrString[30];
+ char publicPortString[40];
+ Property propArgs[8];
+ mDNSu16 ReqPortNum = RequestedPortNum(n);
+ NATTraversalInfo *n2 = m->NATTraversals;
+
+ // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
+ // UPnP gateways will report conflicts if different devices request the same external port, but if two
+ // clients on the same device request the same external port the second one just stomps over the first.
+ // One way this can happen is like this:
+ // 1. Client A binds local port 80
+ // 2. Client A requests external port 80 -> internal port 80
+ // 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
+ // 4. Client A tries again, and successfully gets external port 80 -> internal port 81
+ // 5. Client B on same machine tries to bind local port 80, and fails
+ // 6. Client B tries again, and successfully binds local port 81
+ // 7. Client B now requests external port 81 -> internal port 81
+ // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping
+
+ while (n2)
+ {
+ if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
+ else
+ {
+ if (n->tcpInfo.retries < 100)
+ {
+ n->tcpInfo.retries++;
+ ReqPortNum = RequestedPortNum(n); // Pick a new port number
+ n2 = m->NATTraversals; // And re-scan the list looking for conflicts
+ }
+ else
+ {
+ natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
+ return mStatus_NoError;
+ }
+ }
+ }
+
+ // create strings to use in the message
+ mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum);
+ mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort));
+ mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum);
+ mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
+ m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);
+
+ // build the message
+ mDNSPlatformMemZero(propArgs, sizeof(propArgs));
+ propArgs[0].name = "NewRemoteHost";
+ propArgs[0].type = "string";
+ propArgs[0].value = "";
+ propArgs[1].name = "NewExternalPort";
+ propArgs[1].type = "ui2";
+ propArgs[1].value = externalPort;
+ propArgs[2].name = "NewProtocol";
+ propArgs[2].type = "string";
+ propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
+ propArgs[3].name = "NewInternalPort";
+ propArgs[3].type = "ui2";
+ propArgs[3].value = internalPort;
+ propArgs[4].name = "NewInternalClient";
+ propArgs[4].type = "string";
+ propArgs[4].value = localIPAddrString;
+ propArgs[5].name = "NewEnabled";
+ propArgs[5].type = "boolean";
+ propArgs[5].value = "1";
+ propArgs[6].name = "NewPortMappingDescription";
+ propArgs[6].type = "string";
+ propArgs[6].value = publicPortString;
+ propArgs[7].name = "NewLeaseDuration";
+ propArgs[7].type = "ui4";
+ propArgs[7].value = "0";
+
+ LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
+ return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
+ }
+
+mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n)
+ {
+ LogInfo("LNT_MapPort");
+ if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing
+ n->tcpInfo.parentNATInfo = n;
+ n->tcpInfo.retries = 0;
+ return SendPortMapRequest(m, n);
+ }
+
+mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n)
+ {
+ char externalPort[10];
+ Property propArgs[3];
+ tcpLNTInfo *info;
+ tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList;
+ mStatus err;
+
+ // If no NAT gateway to talk to, no need to do all this work for nothing
+ if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;
+
+ mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));
+
+ mDNSPlatformMemZero(propArgs, sizeof(propArgs));
+ propArgs[0].name = "NewRemoteHost";
+ propArgs[0].type = "string";
+ propArgs[0].value = "";
+ propArgs[1].name = "NewExternalPort";
+ propArgs[1].type = "ui2";
+ propArgs[1].value = externalPort;
+ propArgs[2].name = "NewProtocol";
+ propArgs[2].type = "string";
+ propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
+
+ n->tcpInfo.parentNATInfo = n;
+
+ // clean up previous port mapping requests and allocations
+ if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
+ if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
+ if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; }
+ if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; }
+
+ // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns)
+ if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL)
+ { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
+ *info = n->tcpInfo;
+
+ while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list
+ *infoPtr = info; // append
+
+ err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
+ if (err) DisposeInfoFromUnmapList(m, info);
+ return err;
+ }
+
+mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
+ {
+ return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
+ }
+
+mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
+ {
+ // Device description format -
+ // - device description URL
+ // - host/port
+ static const char szSSDPMsgDescribeDeviceFMT[] =
+ "GET %s HTTP/1.1\r\n"
+ "Accept: text/xml, application/xml\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
+ "Host: %s\r\n"
+ "Connection: close\r\n"
+ "\r\n";
+
+ if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need
+
+ if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }
+
+ // build message
+ if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer
+ else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
+ info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
+ LogInfo("Describe Device: [%s]", info->Request);
+ return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
+ }
+
+// This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response
+// referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and
+// URL info we need.
+mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len)
+ {
+ const mDNSu8 *ptr = data;
+ const mDNSu8 *end = data + len;
+ const mDNSu8 *stop = ptr;
+
+ if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need
+
+ // The formatting of the HTTP header is not always the same when it comes to the placement of
+ // the service and location strings, so we just look for each of them from the beginning for every response
+
+ // figure out if this is a message from a service we care about
+ while (ptr && ptr != end)
+ {
+ if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
+ ptr++;
+ }
+ if (ptr == end)
+ {
+ ptr = data;
+ while (ptr && ptr != end)
+ {
+ if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break;
+ ptr++;
+ }
+ }
+ if (ptr == mDNSNULL || ptr == end) return; // not a message we care about
+
+ // find "Location:", starting from the beginning
+ ptr = data;
+ while (ptr && ptr != end)
+ {
+ if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking
+ ptr++;
+ }
+ if (ptr == mDNSNULL || ptr == end)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", "");
+ return; // not a message we care about
+ }
+ ptr += 9; //Skip over 'Location:'
+ while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces
+ if (ptr >= end) return;
+
+ // find the end of the line
+ for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } }
+
+ // fill in default port
+ m->UPnPRouterPort = mDNSOpaque16fromIntVal(80);
+
+ // free string pointers and set to NULL
+ if (m->UPnPRouterAddressString != mDNSNULL)
+ {
+ mDNSPlatformMemFree(m->UPnPRouterAddressString);
+ m->UPnPRouterAddressString = mDNSNULL;
+ }
+ if (m->UPnPRouterURL != mDNSNULL)
+ {
+ mDNSPlatformMemFree(m->UPnPRouterURL);
+ m->UPnPRouterURL = mDNSNULL;
+ }
+
+ // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc"
+ if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", "");
+ return;
+ }
+
+ m->UPnPInterfaceID = InterfaceID;
+
+ if (m->UPnPRouterAddressString == mDNSNULL)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", "");
+ LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
+ }
+ else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString);
+
+ if (m->UPnPRouterURL == mDNSNULL)
+ {
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", "");
+ LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
+ }
+ else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL);
+
+ LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort));
+ LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID);
+
+ // Don't need the SSDP socket anymore
+ if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+
+ mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", "");
+ // now send message to get the device description
+ GetDeviceDescription(m, &m->tcpDeviceInfo);
+ }
+
+mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
+ {
+ static const char msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "Host:239.255.255.250:1900\r\n"
+ "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n"
+ "Man:\"ssdp:discover\"\r\n"
+ "MX:3\r\n\r\n";
+ static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } };
+
+ mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty
+ unsigned int bufLen;
+
+ if (!mDNSIPPortIsZero(m->UPnPRouterPort))
+ {
+ if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+ if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo);
+ return;
+ }
+
+ // Always query for WANIPConnection in the first SSDP packet
+ if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse;
+
+ // Create message
+ bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
+
+ debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
+
+ if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
+ {
+ if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); }
+ mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort);
+ mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort);
+ }
+
+ m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection;
+ }
+
+mDNSexport void LNT_ClearState(mDNS *const m)
+ {
+ if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; }
+ if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
+ m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports
+ }
+
+#endif /* _LEGACY_NAT_TRAVERSAL_ */
diff --git a/src/tools/mdnssd/Poll.c b/src/tools/mdnssd/Poll.c
new file mode 100644
index 0000000000..9adc632b71
--- /dev/null
+++ b/src/tools/mdnssd/Poll.c
@@ -0,0 +1,728 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Poll.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <process.h>
+#include "GenLinkedList.h"
+#include "DebugServices.h"
+
+
+typedef struct PollSource_struct
+{
+ SOCKET socket;
+ HANDLE handle;
+ void *context;
+
+ union
+ {
+ mDNSPollSocketCallback socket;
+ mDNSPollEventCallback event;
+ } callback;
+
+ struct Worker_struct *worker;
+ struct PollSource_struct *next;
+
+} PollSource;
+
+
+typedef struct Worker_struct
+{
+ HANDLE thread; // NULL for main worker
+ unsigned id; // 0 for main worker
+
+ HANDLE start; // NULL for main worker
+ HANDLE stop; // NULL for main worker
+ BOOL done; // Not used for main worker
+
+ DWORD numSources;
+ PollSource *sources[ MAXIMUM_WAIT_OBJECTS ];
+ HANDLE handles[ MAXIMUM_WAIT_OBJECTS ];
+ DWORD result;
+ struct Worker_struct *next;
+} Worker;
+
+
+typedef struct Poll_struct
+{
+ mDNSBool setup;
+ HANDLE wakeup;
+ GenLinkedList sources;
+ DWORD numSources;
+ Worker main;
+ GenLinkedList workers;
+ HANDLE workerHandles[ MAXIMUM_WAIT_OBJECTS ];
+ DWORD numWorkers;
+
+} Poll;
+
+
+/*
+ * Poll Methods
+ */
+
+mDNSlocal mStatus PollSetup();
+mDNSlocal mStatus PollRegisterSource( PollSource *source );
+mDNSlocal void PollUnregisterSource( PollSource *source );
+mDNSlocal mStatus PollStartWorkers();
+mDNSlocal mStatus PollStopWorkers();
+mDNSlocal void PollRemoveWorker( Worker *worker );
+
+
+/*
+ * Worker Methods
+ */
+
+mDNSlocal mStatus WorkerInit( Worker *worker );
+mDNSlocal void WorkerFree( Worker *worker );
+mDNSlocal void WorkerRegisterSource( Worker *worker, PollSource *source );
+mDNSlocal int WorkerSourceToIndex( Worker *worker, PollSource *source );
+mDNSlocal void WorkerUnregisterSource( Worker *worker, PollSource *source );
+mDNSlocal void WorkerDispatch( Worker *worker);
+mDNSlocal void CALLBACK WorkerWakeupNotification( HANDLE event, void *context );
+mDNSlocal unsigned WINAPI WorkerMain( LPVOID inParam );
+
+
+static void
+ShiftDown( void * arr, size_t arraySize, size_t itemSize, int index )
+{
+ memmove( ( ( unsigned char* ) arr ) + ( ( index - 1 ) * itemSize ), ( ( unsigned char* ) arr ) + ( index * itemSize ), ( arraySize - index ) * itemSize );
+}
+
+
+#define DEBUG_NAME "[mDNSWin32] "
+#define gMDNSRecord mDNSStorage
+mDNSlocal Poll gPoll = { mDNSfalse, NULL };
+
+#define LogErr( err, FUNC ) LogMsg( "%s:%d - %s failed: %d\n", __FUNCTION__, __LINE__, FUNC, err );
+
+
+mStatus
+mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context )
+{
+ PollSource *source = NULL;
+ HANDLE event = INVALID_HANDLE_VALUE;
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ err = PollSetup();
+ require_noerr( err, exit );
+ }
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ event = WSACreateEvent();
+ require_action( event, exit, err = mStatus_NoMemoryErr );
+
+ err = WSAEventSelect( socket, event, networkEvents );
+ require_noerr( err, exit );
+
+ source->socket = socket;
+ source->handle = event;
+ source->callback.socket = callback;
+ source->context = context;
+
+ err = PollRegisterSource( source );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err != mStatus_NoError )
+ {
+ if ( event != INVALID_HANDLE_VALUE )
+ {
+ WSACloseEvent( event );
+ }
+
+ if ( source != NULL )
+ {
+ free( source );
+ }
+ }
+
+ return err;
+}
+
+
+void
+mDNSPollUnregisterSocket( SOCKET socket )
+{
+ PollSource *source;
+
+ for ( source = gPoll.sources.Head; source; source = source->next )
+ {
+ if ( source->socket == socket )
+ {
+ break;
+ }
+ }
+
+ if ( source )
+ {
+ WSACloseEvent( source->handle );
+ PollUnregisterSource( source );
+ free( source );
+ }
+}
+
+
+mStatus
+mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context )
+{
+ PollSource *source = NULL;
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ err = PollSetup();
+ require_noerr( err, exit );
+ }
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ source->socket = INVALID_SOCKET;
+ source->handle = event;
+ source->callback.event = callback;
+ source->context = context;
+
+ err = PollRegisterSource( source );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err != mStatus_NoError )
+ {
+ if ( source != NULL )
+ {
+ free( source );
+ }
+ }
+
+ return err;
+}
+
+
+void
+mDNSPollUnregisterEvent( HANDLE event )
+{
+ PollSource *source;
+
+ for ( source = gPoll.sources.Head; source; source = source->next )
+ {
+ if ( source->handle == event )
+ {
+ break;
+ }
+ }
+
+ if ( source )
+ {
+ PollUnregisterSource( source );
+ free( source );
+ }
+}
+
+
+mStatus
+mDNSPoll( DWORD msec )
+{
+ mStatus err = mStatus_NoError;
+
+ if ( gPoll.numWorkers > 0 )
+ {
+ err = PollStartWorkers();
+ require_noerr( err, exit );
+ }
+
+ gPoll.main.result = WaitForMultipleObjects( gPoll.main.numSources, gPoll.main.handles, FALSE, msec );
+ err = translate_errno( ( gPoll.main.result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForMultipleObjects()" );
+ require_action( gPoll.main.result != WAIT_FAILED, exit, err = ( mStatus ) GetLastError() );
+
+ if ( gPoll.numWorkers > 0 )
+ {
+ err = PollStopWorkers();
+ require_noerr( err, exit );
+ }
+
+ WorkerDispatch( &gPoll.main );
+
+exit:
+
+ return ( err );
+}
+
+
+mDNSlocal mStatus
+PollSetup()
+{
+ mStatus err = mStatus_NoError;
+
+ if ( !gPoll.setup )
+ {
+ memset( &gPoll, 0, sizeof( gPoll ) );
+
+ InitLinkedList( &gPoll.sources, offsetof( PollSource, next ) );
+ InitLinkedList( &gPoll.workers, offsetof( Worker, next ) );
+
+ gPoll.wakeup = CreateEvent( NULL, TRUE, FALSE, NULL );
+ require_action( gPoll.wakeup, exit, err = mStatus_NoMemoryErr );
+
+ err = WorkerInit( &gPoll.main );
+ require_noerr( err, exit );
+
+ gPoll.setup = mDNStrue;
+ }
+
+exit:
+
+ return err;
+}
+
+
+mDNSlocal mStatus
+PollRegisterSource( PollSource *source )
+{
+ Worker *worker = NULL;
+ mStatus err = mStatus_NoError;
+
+ AddToTail( &gPoll.sources, source );
+ gPoll.numSources++;
+
+ // First check our main worker. In most cases, we won't have to worry about threads
+
+ if ( gPoll.main.numSources < MAXIMUM_WAIT_OBJECTS )
+ {
+ WorkerRegisterSource( &gPoll.main, source );
+ }
+ else
+ {
+ // Try to find a thread to use that we've already created
+
+ for ( worker = gPoll.workers.Head; worker; worker = worker->next )
+ {
+ if ( worker->numSources < MAXIMUM_WAIT_OBJECTS )
+ {
+ WorkerRegisterSource( worker, source );
+ break;
+ }
+ }
+
+ // If not, then create a worker and make a thread to run it in
+
+ if ( !worker )
+ {
+ worker = ( Worker* ) malloc( sizeof( Worker ) );
+ require_action( worker, exit, err = mStatus_NoMemoryErr );
+
+ memset( worker, 0, sizeof( Worker ) );
+
+ worker->start = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( worker->start, exit, err = mStatus_NoMemoryErr );
+
+ worker->stop = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( worker->stop, exit, err = mStatus_NoMemoryErr );
+
+ err = WorkerInit( worker );
+ require_noerr( err, exit );
+
+ // Create thread with _beginthreadex() instead of CreateThread() to avoid
+ // memory leaks when using static run-time libraries.
+ // See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+
+ worker->thread = ( HANDLE ) _beginthreadex_compat( NULL, 0, WorkerMain, worker, 0, &worker->id );
+ err = translate_errno( worker->thread, ( mStatus ) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ AddToTail( &gPoll.workers, worker );
+ gPoll.workerHandles[ gPoll.numWorkers++ ] = worker->stop;
+
+ WorkerRegisterSource( worker, source );
+ }
+ }
+
+exit:
+
+ if ( err && worker )
+ {
+ WorkerFree( worker );
+ }
+
+ return err;
+}
+
+
+mDNSlocal void
+PollUnregisterSource( PollSource *source )
+{
+ RemoveFromList( &gPoll.sources, source );
+ gPoll.numSources--;
+
+ WorkerUnregisterSource( source->worker, source );
+}
+
+
+mDNSlocal mStatus
+PollStartWorkers()
+{
+ Worker *worker;
+ mStatus err = mStatus_NoError;
+ BOOL ok;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "starting workers\n" );
+
+ worker = gPoll.workers.Head;
+
+ while ( worker )
+ {
+ Worker *next = worker->next;
+
+ if ( worker->numSources == 1 )
+ {
+ PollRemoveWorker( worker );
+ }
+ else
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "waking up worker\n" );
+
+ ok = SetEvent( worker->start );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ if ( err )
+ {
+ PollRemoveWorker( worker );
+ }
+ }
+
+ worker = next;
+ }
+
+ err = mStatus_NoError;
+
+ return err;
+}
+
+
+mDNSlocal mStatus
+PollStopWorkers()
+{
+ DWORD result;
+ Worker *worker;
+ BOOL ok;
+ mStatus err = mStatus_NoError;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "stopping workers\n" );
+
+ ok = SetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ // Wait For 5 seconds for all the workers to wake up
+
+ result = WaitForMultipleObjects( gPoll.numWorkers, gPoll.workerHandles, TRUE, 5000 );
+ err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForMultipleObjects()" );
+
+ ok = ResetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "ResetEvent()" );
+
+ for ( worker = gPoll.workers.Head; worker; worker = worker->next )
+ {
+ WorkerDispatch( worker );
+ }
+
+ err = mStatus_NoError;
+
+ return err;
+}
+
+
+mDNSlocal void
+PollRemoveWorker( Worker *worker )
+{
+ DWORD result;
+ mStatus err;
+ BOOL ok;
+ DWORD i;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "removing worker %d\n", worker->id );
+
+ RemoveFromList( &gPoll.workers, worker );
+
+ // Remove handle from gPoll.workerHandles
+
+ for ( i = 0; i < gPoll.numWorkers; i++ )
+ {
+ if ( gPoll.workerHandles[ i ] == worker->stop )
+ {
+ ShiftDown( gPoll.workerHandles, gPoll.numWorkers, sizeof( gPoll.workerHandles[ 0 ] ), i + 1 );
+ break;
+ }
+ }
+
+ worker->done = TRUE;
+ gPoll.numWorkers--;
+
+ // Cause the thread to exit.
+
+ ok = SetEvent( worker->start );
+ err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "SetEvent()" );
+
+ result = WaitForSingleObject( worker->thread, 5000 );
+ err = translate_errno( result != WAIT_FAILED, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "WaitForSingleObject()" );
+
+ if ( ( result == WAIT_FAILED ) || ( result == WAIT_TIMEOUT ) )
+ {
+ ok = TerminateThread( worker->thread, 0 );
+ err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
+ if ( err ) LogErr( err, "TerminateThread()" );
+ }
+
+ CloseHandle( worker->thread );
+ worker->thread = NULL;
+
+ WorkerFree( worker );
+}
+
+
+mDNSlocal void
+WorkerRegisterSource( Worker *worker, PollSource *source )
+{
+ source->worker = worker;
+ worker->sources[ worker->numSources ] = source;
+ worker->handles[ worker->numSources ] = source->handle;
+ worker->numSources++;
+}
+
+
+mDNSlocal int
+WorkerSourceToIndex( Worker *worker, PollSource *source )
+{
+ int index;
+
+ for ( index = 0; index < ( int ) worker->numSources; index++ )
+ {
+ if ( worker->sources[ index ] == source )
+ {
+ break;
+ }
+ }
+
+ if ( index == ( int ) worker->numSources )
+ {
+ index = -1;
+ }
+
+ return index;
+}
+
+
+mDNSlocal void
+WorkerUnregisterSource( Worker *worker, PollSource *source )
+{
+ int sourceIndex = WorkerSourceToIndex( worker, source );
+ DWORD delta;
+
+ if ( sourceIndex == -1 )
+ {
+ LogMsg( "WorkerUnregisterSource: source not found in list" );
+ goto exit;
+ }
+
+ delta = ( worker->numSources - sourceIndex - 1 );
+
+ // If this source is not at the end of the list, then move memory
+
+ if ( delta > 0 )
+ {
+ ShiftDown( worker->sources, worker->numSources, sizeof( worker->sources[ 0 ] ), sourceIndex + 1 );
+ ShiftDown( worker->handles, worker->numSources, sizeof( worker->handles[ 0 ] ), sourceIndex + 1 );
+ }
+
+ worker->numSources--;
+
+exit:
+
+ return;
+}
+
+
+mDNSlocal void CALLBACK
+WorkerWakeupNotification( HANDLE event, void *context )
+{
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "Worker thread wakeup\n" );
+}
+
+
+mDNSlocal void
+WorkerDispatch( Worker *worker )
+{
+ if ( worker->result == WAIT_FAILED )
+ {
+ /* What should we do here? */
+ }
+ else if ( worker->result == WAIT_TIMEOUT )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "timeout\n" );
+ }
+ else
+ {
+ DWORD waitItemIndex = ( DWORD )( ( ( int ) worker->result ) - WAIT_OBJECT_0 );
+ PollSource *source = NULL;
+
+ // Sanity check
+
+ if ( waitItemIndex >= worker->numSources )
+ {
+ LogMsg( "WorkerDispatch: waitItemIndex (%d) is >= numSources (%d)", waitItemIndex, worker->numSources );
+ goto exit;
+ }
+
+ source = worker->sources[ waitItemIndex ];
+
+ if ( source->socket != INVALID_SOCKET )
+ {
+ WSANETWORKEVENTS event;
+
+ if ( WSAEnumNetworkEvents( source->socket, source->handle, &event ) == 0 )
+ {
+ source->callback.socket( source->socket, &event, source->context );
+ }
+ else
+ {
+ source->callback.socket( source->socket, NULL, source->context );
+ }
+ }
+ else
+ {
+ source->callback.event( source->handle, source->context );
+ }
+ }
+
+exit:
+
+ return;
+}
+
+
+mDNSlocal mStatus
+WorkerInit( Worker *worker )
+{
+ PollSource *source = NULL;
+ mStatus err = mStatus_NoError;
+
+ require_action( worker, exit, err = mStatus_BadParamErr );
+
+ source = malloc( sizeof( PollSource ) );
+ require_action( source, exit, err = mStatus_NoMemoryErr );
+
+ source->socket = INVALID_SOCKET;
+ source->handle = gPoll.wakeup;
+ source->callback.event = WorkerWakeupNotification;
+ source->context = NULL;
+
+ WorkerRegisterSource( worker, source );
+
+exit:
+
+ return err;
+}
+
+
+mDNSlocal void
+WorkerFree( Worker *worker )
+{
+ if ( worker->start )
+ {
+ CloseHandle( worker->start );
+ worker->start = NULL;
+ }
+
+ if ( worker->stop )
+ {
+ CloseHandle( worker->stop );
+ worker->stop = NULL;
+ }
+
+ free( worker );
+}
+
+
+mDNSlocal unsigned WINAPI
+WorkerMain( LPVOID inParam )
+{
+ Worker *worker = ( Worker* ) inParam;
+ mStatus err = mStatus_NoError;
+
+ require_action( worker, exit, err = mStatus_BadParamErr );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME, "entering WorkerMain()\n" );
+
+ while ( TRUE )
+ {
+ DWORD result;
+ BOOL ok;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d will wait on main loop\n", worker->id );
+
+ result = WaitForSingleObject( worker->start, INFINITE );
+ err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "WaitForSingleObject()" ); break; }
+ if ( worker->done ) break;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d will wait on sockets\n", worker->id );
+
+ worker->result = WaitForMultipleObjects( worker->numSources, worker->handles, FALSE, INFINITE );
+ err = translate_errno( ( worker->result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "WaitForMultipleObjects()" ); break; }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d did wait on sockets: %d\n", worker->id, worker->result );
+
+ ok = SetEvent( gPoll.wakeup );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "SetEvent()" ); break; }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d preparing to sleep\n", worker->id );
+
+ ok = SetEvent( worker->stop );
+ err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
+ if ( err ) { LogErr( err, "SetEvent()" ); break; }
+ }
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "exiting WorkerMain()\n" );
+
+exit:
+
+ return 0;
+}
diff --git a/src/tools/mdnssd/Poll.h b/src/tools/mdnssd/Poll.h
new file mode 100644
index 0000000000..bd1b10fc22
--- /dev/null
+++ b/src/tools/mdnssd/Poll.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _Poll_h
+#define _Poll_h
+
+#include "CommonServices.h"
+#include <mswsock.h>
+#include "mDNSEmbeddedAPI.h"
+#include "uDNS.h"
+
+
+#if defined(__cplusplus )
+extern "C" {
+#endif
+
+
+typedef void ( CALLBACK *mDNSPollSocketCallback )( SOCKET socket, LPWSANETWORKEVENTS event, void *context );
+typedef void ( CALLBACK *mDNSPollEventCallback )( HANDLE event, void *context );
+
+
+extern mStatus
+mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context );
+
+
+extern void
+mDNSPollUnregisterSocket( SOCKET socket );
+
+
+extern mStatus
+mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context );
+
+
+extern void
+mDNSPollUnregisterEvent( HANDLE event );
+
+
+extern mStatus
+mDNSPoll( DWORD msec );
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif
diff --git a/src/tools/mdnssd/PosixDaemon.c b/src/tools/mdnssd/PosixDaemon.c
index acea1adb02..4748fa53df 100644
--- a/src/tools/mdnssd/PosixDaemon.c
+++ b/src/tools/mdnssd/PosixDaemon.c
@@ -37,6 +37,8 @@
#include <fcntl.h>
#include <pwd.h>
#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
#if __APPLE__
#undef daemon
@@ -168,10 +170,132 @@ mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
return EINTR;
}
+
+static int kill_other()
+ {
+ // tries to kill other mdnssd instances
+ enum { maxPath = 1024 };
+ mStatus res = 0;
+ const char * dirPath = "/proc";
+ DIR *dirp;
+ struct dirent *next;
+ char filePath[maxPath];
+ char *baseN = filePath;
+ size_t maxName = maxPath;
+ const char *dAtt = dirPath;
+ pid_t myPid = getpid();
+ char refPath[128];
+ char refLine[128];
+ char line[128];
+
+ snprintf(refPath, sizeof(refPath), "/proc/%lu/status",(unsigned long)myPid);
+ FILE *f = fopen(refPath, "r");
+ if (!f)
+ {
+ LogMsg("mdnssd kill_other failed to get ref line");
+ return 1;
+ }
+ if (!fgets(refLine,sizeof(refLine),f)) {
+ LogMsg("mdnssd kill_other got empty ref line");
+ return 2;
+ }
+ if (strncmp("Name:", refLine, 5)) {
+ LogMsg("mdnssd unexpected format of first line of /proc/*/status");
+ return 4;
+ }
+ fclose(f);
+ for (; maxName != 0; --maxName)
+ {
+ if ((*dAtt) == 0)
+ break;
+ *baseN++ = *dAtt++;
+ }
+ if (maxName>0)
+ {
+ --maxName;
+ *baseN++ = '/';
+ }
+ const char *statusFile = "/status";
+ size_t lenStatusFile = strlen(statusFile);
+ maxName -= lenStatusFile;
+
+ dirp = opendir (dirPath);
+ if (!dirp)
+ {
+ LogMsg("cannot open directory %s", dirPath);
+ return 3;
+ }
+ while (1)
+ {
+ size_t lenF;
+ errno = 0;
+ next = readdir (dirp);
+ if (!next)
+ {
+ if (errno == EOVERFLOW)
+ {
+ continue;
+ }
+ else
+ {
+ if (errno != 0)
+ {
+ char errStr[128];
+ LogMsg("Error reading /proc directory, %s",strerror_r(errno, errStr, sizeof(errStr)));
+ res=3;
+ }
+ break;
+ }
+ }
+ switch (next->d_type)
+ {
+ case DT_LNK:
+ case DT_DIR:
+ lenF=strlen(next->d_name);
+ if (lenF < maxName)
+ {
+ char *endP = 0;
+ unsigned long pidV = strtoul(next->d_name, &endP , 10);
+ if ((size_t)(endP - next->d_name) == lenF)
+ {
+ pid_t pidAtt = (pid_t)pidV;
+ if (pidAtt != myPid)
+ {
+ size_t i;
+ for (i = 0; i < lenF; ++ i)
+ baseN[i] = next->d_name[i];
+ for (i = 0; i <= lenStatusFile; ++i)
+ baseN[i + lenF] = statusFile[i];
+ f = fopen(filePath,"r");
+ if (f)
+ {
+ if (fgets(line,sizeof(line),f) && strcmp(refLine, line) == 0)
+ {
+ LogMsg("killing old mdnssd process with pid %d\n", pidAtt);
+ kill(pidAtt, 9);
+ }
+ fclose(f);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (closedir (dirp) != 0)
+ LogMsg("error closing directory %s", dirPath);
+ return res;
+ }
+
int main(int argc, char **argv)
{
mStatus err;
+ kill_other();
+
ParseCmdLinArgs(argc, argv);
LogMsg("%s starting", mDNSResponderVersionString);
diff --git a/src/tools/mdnssd/RegNames.h b/src/tools/mdnssd/RegNames.h
new file mode 100644
index 0000000000..bc885d61e5
--- /dev/null
+++ b/src/tools/mdnssd/RegNames.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//----------------------------------------------------------------------------------------
+// Registry Constants
+//----------------------------------------------------------------------------------------
+
+#if defined(UNICODE)
+
+# define kServiceParametersSoftware L"SOFTWARE"
+# define kServiceParametersAppleComputer L"Apple Computer, Inc."
+# define kServiceParametersBonjour L"Bonjour"
+# define kServiceParametersNode L"SOFTWARE\\Apple Inc.\\Bonjour"
+# define kServiceName L"Bonjour Service"
+# define kServiceDynDNSBrowseDomains L"BrowseDomains"
+# define kServiceDynDNSHostNames L"HostNames"
+# define kServiceDynDNSRegistrationDomains L"RegistrationDomains"
+# define kServiceDynDNSDomains L"Domains" // value is comma separated list of domains
+# define kServiceDynDNSEnabled L"Enabled"
+# define kServiceDynDNSStatus L"Status"
+# define kServiceManageLLRouting L"ManageLLRouting"
+# define kServiceCacheEntryCount L"CacheEntryCount"
+# define kServiceManageFirewall L"ManageFirewall"
+# define kServiceAdvertisedServices L"Services"
+
+# else
+
+# define kServiceParametersSoftware "SOFTWARE"
+# define kServiceParametersAppleComputer "Apple Computer, Inc."
+# define kServiceParametersBonjour "Bonjour"
+# define kServiceParametersNode "SOFTWARE\\Apple Inc.\\Bonjour"
+# define kServiceName "Bonjour Service"
+# define kServiceDynDNSBrowseDomains "BrowseDomains"
+# define kServiceDynDNSHostNames "HostNames"
+# define kServiceDynDNSRegistrationDomains "RegistrationDomains"
+# define kServiceDynDNSDomains "Domains" // value is comma separated list of domains
+# define kServiceDynDNSEnabled "Enabled"
+# define kServiceDynDNSStatus "Status"
+# define kServiceManageLLRouting "ManageLLRouting"
+# define kServiceCacheEntryCount "CacheEntryCount"
+# define kServiceManageFirewall "ManageFirewall"
+
+#endif
diff --git a/src/tools/mdnssd/Secret.c b/src/tools/mdnssd/Secret.c
new file mode 100644
index 0000000000..5abd28b39f
--- /dev/null
+++ b/src/tools/mdnssd/Secret.c
@@ -0,0 +1,338 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Secret.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <process.h>
+#include <ntsecapi.h>
+#include <lm.h>
+#include "DebugServices.h"
+
+
+mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input );
+mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
+
+
+BOOL
+LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
+{
+ PLSA_UNICODE_STRING domainLSA;
+ PLSA_UNICODE_STRING keyLSA;
+ PLSA_UNICODE_STRING secretLSA;
+ size_t i;
+ size_t dlen;
+ LSA_OBJECT_ATTRIBUTES attrs;
+ LSA_HANDLE handle = NULL;
+ NTSTATUS res;
+ OSStatus err;
+
+ check( inDomain );
+ check( outDomain );
+ check( outKey );
+ check( outSecret );
+
+ // Initialize
+
+ domainLSA = NULL;
+ keyLSA = NULL;
+ secretLSA = NULL;
+
+ // Make sure we have enough space to add trailing dot
+
+ dlen = strlen( inDomain );
+ err = strcpy_s( outDomain, outDomainSize - 2, inDomain );
+ require_noerr( err, exit );
+
+ // If there isn't a trailing dot, add one because the mDNSResponder
+ // presents names with the trailing dot.
+
+ if ( outDomain[ dlen - 1 ] != '.' )
+ {
+ outDomain[ dlen++ ] = '.';
+ outDomain[ dlen ] = '\0';
+ }
+
+ // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
+
+ for ( i = 0; i < dlen; i++ )
+ {
+ outDomain[i] = (char) tolower( outDomain[i] ); // canonicalize -> lower case
+ }
+
+ // attrs are reserved, so initialize to zeroes.
+
+ ZeroMemory( &attrs, sizeof( attrs ) );
+
+ // Get a handle to the Policy object on the local system
+
+ res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Get the encrypted data
+
+ domainLSA = ( PLSA_UNICODE_STRING ) malloc( sizeof( LSA_UNICODE_STRING ) );
+ require_action( domainLSA != NULL, exit, err = mStatus_NoMemoryErr );
+ err = MakeLsaStringFromUTF8String( domainLSA, outDomain );
+ require_noerr( err, exit );
+
+ // Retrieve the key
+
+ res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr_quiet( err, exit );
+
+ // <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
+ // make sure it doesn't conflict with a zone name.
+ // Strip off the "$" prefix.
+
+ err = MakeUTF8StringFromLsaString( outKey, outKeySize, keyLSA );
+ require_noerr( err, exit );
+ require_action( outKey[0] == '$', exit, err = kUnknownErr );
+ memcpy( outKey, outKey + 1, strlen( outKey ) );
+
+ // Retrieve the secret
+
+ res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr_quiet( err, exit );
+
+ // Convert the secret to UTF8 string
+
+ err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( domainLSA != NULL )
+ {
+ if ( domainLSA->Buffer != NULL )
+ {
+ free( domainLSA->Buffer );
+ }
+
+ free( domainLSA );
+ }
+
+ if ( keyLSA != NULL )
+ {
+ LsaFreeMemory( keyLSA );
+ }
+
+ if ( secretLSA != NULL )
+ {
+ LsaFreeMemory( secretLSA );
+ }
+
+ if ( handle )
+ {
+ LsaClose( handle );
+ handle = NULL;
+ }
+
+ return ( !err ) ? TRUE : FALSE;
+}
+
+
+mDNSBool
+LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
+{
+ size_t inDomainLength;
+ size_t inKeyLength;
+ char domain[ 1024 ];
+ char key[ 1024 ];
+ LSA_OBJECT_ATTRIBUTES attrs;
+ LSA_HANDLE handle = NULL;
+ NTSTATUS res;
+ LSA_UNICODE_STRING lucZoneName;
+ LSA_UNICODE_STRING lucKeyName;
+ LSA_UNICODE_STRING lucSecretName;
+ BOOL ok = TRUE;
+ OSStatus err;
+
+ require_action( inDomain != NULL, exit, ok = FALSE );
+ require_action( inKey != NULL, exit, ok = FALSE );
+ require_action( inSecret != NULL, exit, ok = FALSE );
+
+ // If there isn't a trailing dot, add one because the mDNSResponder
+ // presents names with the trailing dot.
+
+ ZeroMemory( domain, sizeof( domain ) );
+ inDomainLength = strlen( inDomain );
+ require_action( inDomainLength > 0, exit, ok = FALSE );
+ err = strcpy_s( domain, sizeof( domain ) - 2, inDomain );
+ require_action( !err, exit, ok = FALSE );
+
+ if ( domain[ inDomainLength - 1 ] != '.' )
+ {
+ domain[ inDomainLength++ ] = '.';
+ domain[ inDomainLength ] = '\0';
+ }
+
+ // <rdar://problem/4192119>
+ //
+ // Prepend "$" to the key name, so that there will
+ // be no conflict between the zone name and the key
+ // name
+
+ ZeroMemory( key, sizeof( key ) );
+ inKeyLength = strlen( inKey );
+ require_action( inKeyLength > 0 , exit, ok = FALSE );
+ key[ 0 ] = '$';
+ err = strcpy_s( key + 1, sizeof( key ) - 3, inKey );
+ require_action( !err, exit, ok = FALSE );
+ inKeyLength++;
+
+ if ( key[ inKeyLength - 1 ] != '.' )
+ {
+ key[ inKeyLength++ ] = '.';
+ key[ inKeyLength ] = '\0';
+ }
+
+ // attrs are reserved, so initialize to zeroes.
+
+ ZeroMemory( &attrs, sizeof( attrs ) );
+
+ // Get a handle to the Policy object on the local system
+
+ res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Intializing PLSA_UNICODE_STRING structures
+
+ err = MakeLsaStringFromUTF8String( &lucZoneName, domain );
+ require_noerr( err, exit );
+
+ err = MakeLsaStringFromUTF8String( &lucKeyName, key );
+ require_noerr( err, exit );
+
+ err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret );
+ require_noerr( err, exit );
+
+ // Store the private data.
+
+ res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr( err, exit );
+
+ res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName );
+ err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( handle )
+ {
+ LsaClose( handle );
+ handle = NULL;
+ }
+
+ return ok;
+}
+
+
+//===========================================================================================================================
+// MakeLsaStringFromUTF8String
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input )
+{
+ int size;
+ OSStatus err;
+
+ check( input );
+ check( output );
+
+ output->Buffer = NULL;
+
+ size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 );
+ err = translate_errno( size > 0, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ output->Length = (USHORT)( size * sizeof( wchar_t ) );
+ output->Buffer = (PWCHAR) malloc( output->Length );
+ require_action( output->Buffer, exit, err = mStatus_NoMemoryErr );
+ size = MultiByteToWideChar( CP_UTF8, 0, input, -1, output->Buffer, size );
+ err = translate_errno( size > 0, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // We're going to subtrace one wchar_t from the size, because we didn't
+ // include it when we encoded the string
+
+ output->MaximumLength = output->Length;
+ output->Length -= sizeof( wchar_t );
+
+exit:
+
+ if ( err && output->Buffer )
+ {
+ free( output->Buffer );
+ output->Buffer = NULL;
+ }
+
+ return( err );
+}
+
+
+
+//===========================================================================================================================
+// MakeUTF8StringFromLsaString
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input )
+{
+ size_t size;
+ OSStatus err = kNoErr;
+
+ // The Length field of this structure holds the number of bytes,
+ // but WideCharToMultiByte expects the number of wchar_t's. So
+ // we divide by sizeof(wchar_t) to get the correct number.
+
+ size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL);
+ err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Ensure that we have enough space (Add one for trailing '\0')
+
+ require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr );
+
+ // Convert the string
+
+ size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL);
+ err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // have to add the trailing 0 because WideCharToMultiByte doesn't do it,
+ // although it does return the correct size
+
+ output[size] = '\0';
+
+exit:
+
+ return err;
+}
+
diff --git a/src/tools/mdnssd/Secret.h b/src/tools/mdnssd/Secret.h
new file mode 100644
index 0000000000..79643d6242
--- /dev/null
+++ b/src/tools/mdnssd/Secret.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _Secret_h
+#define _Secret_h
+
+#include "mDNSEmbeddedAPI.h"
+
+
+#if defined(__cplusplus )
+extern "C" {
+#endif
+
+
+extern mDNSBool
+LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength );
+
+
+extern mDNSBool
+LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret );
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif \ No newline at end of file
diff --git a/src/tools/mdnssd/Service.c b/src/tools/mdnssd/Service.c
new file mode 100644
index 0000000000..406c4e1cd3
--- /dev/null
+++ b/src/tools/mdnssd/Service.c
@@ -0,0 +1,2597 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <crtdbg.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "Poll.h"
+#include "CommonServices.h"
+#include "DebugServices.h"
+#include "RegNames.h"
+
+#include "uds_daemon.h"
+#include "GenLinkedList.h"
+#include "Service.h"
+#include "EventLog.h"
+
+#include "Resource.h"
+
+#include "mDNSEmbeddedAPI.h"
+#include "uDNS.h"
+#include "mDNSWin32.h"
+#include "mDNSDebug.h"
+
+#include "Firewall.h"
+
+#if( !TARGET_OS_WINDOWS_CE )
+ #include <mswsock.h>
+ #include <process.h>
+ #include <ipExport.h>
+ #include <ws2def.h>
+ #include <ws2ipdef.h>
+ #include <iphlpapi.h>
+ #include <netioapi.h>
+ #include <iptypes.h>
+ #include <powrprof.h>
+#endif
+
+#ifndef HeapEnableTerminationOnCorruption
+# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#define DEBUG_NAME "[mDNSWin32] "
+#define kServiceFirewallName L"Bonjour"
+#define kServiceDependencies TEXT("Tcpip\0\0")
+#define kDNSServiceCacheEntryCountDefault 512
+#define kRetryFirewallPeriod 30 * 1000
+#define kDefValueSize MAX_PATH + 1
+#define kZeroIndex 0
+#define kDefaultRouteMetric 399
+#define kSecondsTo100NSUnits ( 10 * 1000 * 1000 )
+#define kSPSMaintenanceWakePeriod -30
+#define kWaitToRetry (60 * 5)
+
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+#if 0
+#pragma mark == Structures ==
+#endif
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+static void Usage( void );
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent );
+static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath );
+static OSStatus RemoveService( LPCTSTR inName );
+static OSStatus SetServiceParameters();
+static OSStatus GetServiceParameters();
+static OSStatus CheckFirewall();
+static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription );
+static void ReportStatus( int inType, const char *inFormat, ... );
+
+static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] );
+static OSStatus ServiceSetupEventLogging( void );
+static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext );
+
+static OSStatus ServiceRun( int argc, LPTSTR argv[] );
+static void ServiceStop( void );
+
+static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] );
+static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] );
+static OSStatus ServiceSpecificStop( void );
+static void ServiceSpecificFinalize( int argc, LPTSTR argv[] );
+static mStatus SetupServiceEvents();
+static mStatus TearDownServiceEvents();
+static mStatus SetupNotifications();
+static mStatus TearDownNotifications();
+static void CALLBACK StopNotification( HANDLE event, void * context );
+static void CALLBACK PowerSuspendNotification( HANDLE event, void * context );
+static void CALLBACK PowerResumeNotification( HANDLE event, void * context );
+static void CALLBACK InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context );
+static void CALLBACK ComputerDescriptionNotification( HANDLE event, void *context );
+static void CALLBACK TCPChangedNotification( HANDLE event, void *context );
+static void CALLBACK DDNSChangedNotification( HANDLE event, void *context );
+static void CALLBACK FileSharingChangedNotification( HANDLE event, void *context );
+static void CALLBACK FirewallChangedNotification( HANDLE event, void *context );
+static void CALLBACK AdvertisedServicesChangedNotification( HANDLE event, void *context );
+static void CALLBACK SPSWakeupNotification( HANDLE event, void *context );
+static void CALLBACK SPSSleepNotification( HANDLE event, void *context );
+static void CALLBACK UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
+static void CALLBACK UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
+static void CoreCallback(mDNS * const inMDNS, mStatus result);
+static mDNSu8 SystemWakeForNetworkAccess( LARGE_INTEGER * timeout );
+static OSStatus GetRouteDestination(DWORD * ifIndex, DWORD * address);
+static OSStatus SetLLRoute( mDNS * const inMDNS );
+static bool HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric );
+static bool IsValidAddress( const char * addr );
+static bool IsNortelVPN( IP_ADAPTER_INFO * pAdapter );
+static bool IsJuniperVPN( IP_ADAPTER_INFO * pAdapter );
+static bool IsCiscoVPN( IP_ADAPTER_INFO * pAdapter );
+static const char * strnistr( const char * string, const char * subString, size_t max );
+
+#if defined(UNICODE)
+# define StrLen(X) wcslen(X)
+# define StrCmp(X,Y) wcscmp(X,Y)
+#else
+# define StrLen(X) strlen(X)
+# define StrCmp(X,Y) strcmp(X,Y)
+#endif
+
+
+#define kLLNetworkAddr "169.254.0.0"
+#define kLLNetworkAddrMask "255.255.0.0"
+
+
+#include "mDNSEmbeddedAPI.h"
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+#define gMDNSRecord mDNSStorage
+DEBUG_LOCAL mDNS_PlatformSupport gPlatformStorage;
+DEBUG_LOCAL BOOL gServiceQuietMode = FALSE;
+DEBUG_LOCAL SERVICE_TABLE_ENTRY gServiceDispatchTable[] =
+{
+ { kServiceName, ServiceMain },
+ { NULL, NULL }
+};
+DEBUG_LOCAL HANDLE gStopEvent = NULL;
+DEBUG_LOCAL HANDLE gPowerSuspendEvent = NULL;
+DEBUG_LOCAL HANDLE gPowerSuspendAckEvent = NULL;
+DEBUG_LOCAL HANDLE gPowerResumeEvent = NULL;
+DEBUG_LOCAL SOCKET gInterfaceListChangedSocket = INVALID_SOCKET;
+DEBUG_LOCAL HKEY gDescKey = NULL;
+DEBUG_LOCAL HANDLE gDescChangedEvent = NULL; // Computer description changed event
+DEBUG_LOCAL HKEY gTcpipKey = NULL;
+DEBUG_LOCAL HANDLE gTcpipChangedEvent = NULL; // TCP/IP config changed
+DEBUG_LOCAL HKEY gDdnsKey = NULL;
+DEBUG_LOCAL HANDLE gDdnsChangedEvent = NULL; // DynDNS config changed
+DEBUG_LOCAL HKEY gFileSharingKey = NULL;
+DEBUG_LOCAL HANDLE gFileSharingChangedEvent = NULL; // File Sharing changed
+DEBUG_LOCAL HKEY gFirewallKey = NULL;
+DEBUG_LOCAL HANDLE gFirewallChangedEvent = NULL; // Firewall changed
+DEBUG_LOCAL HKEY gAdvertisedServicesKey = NULL;
+DEBUG_LOCAL HANDLE gAdvertisedServicesChangedEvent = NULL; // Advertised services changed
+DEBUG_LOCAL SERVICE_STATUS gServiceStatus;
+DEBUG_LOCAL SERVICE_STATUS_HANDLE gServiceStatusHandle = NULL;
+DEBUG_LOCAL HANDLE gServiceEventSource = NULL;
+DEBUG_LOCAL bool gServiceAllowRemote = false;
+DEBUG_LOCAL int gServiceCacheEntryCount = 0; // 0 means to use the DNS-SD default.
+DEBUG_LOCAL bool gServiceManageLLRouting = true;
+DEBUG_LOCAL HANDLE gSPSWakeupEvent = NULL;
+DEBUG_LOCAL HANDLE gSPSSleepEvent = NULL;
+DEBUG_LOCAL SocketRef gUDSSocket = 0;
+DEBUG_LOCAL udsEventCallback gUDSCallback = NULL;
+DEBUG_LOCAL BOOL gRetryFirewall = FALSE;
+DEBUG_LOCAL BOOL gJustCheckFirewall = FALSE;
+
+typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW );
+mDNSlocal HMODULE gIPHelperLibraryInstance = NULL;
+mDNSlocal GetIpInterfaceEntryFunctionPtr gGetIpInterfaceEntryFunctionPtr = NULL;
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// Main
+//===========================================================================================================================
+int Main( int argc, LPTSTR argv[] )
+{
+ OSStatus err;
+// BOOL ok;
+ BOOL start;
+ int i;
+
+ HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
+
+ debug_initialize( kDebugOutputTypeMetaConsole );
+ debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
+
+ // Default to automatically starting the service dispatcher if no extra arguments are specified.
+
+ start = ( argc <= 1 );
+
+ // Parse arguments.
+
+ for( i = 1; i < argc; ++i )
+ {
+ if( StrCmp( argv[ i ], TEXT("-check-firewall") ) == 0 ) // Check firewall
+ {
+ gJustCheckFirewall = TRUE;
+ start = TRUE;
+ break;
+ } else
+ if( StrCmp( argv[ i ], TEXT("-start") ) == 0 ) // Start
+ {
+ start = TRUE;
+ }
+ else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 ) // Server
+ {
+ err = RunDirect( argc, argv );
+ if( err )
+ {
+ ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err );
+ }
+ goto exit;
+ }
+ else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 ) // Quiet Mode (toggle)
+ {
+ gServiceQuietMode = !gServiceQuietMode;
+ }
+ else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || // Help
+ ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) )
+ {
+ Usage();
+ err = 0;
+ break;
+ }
+ else
+ {
+ Usage();
+ err = kParamErr;
+ break;
+ }
+ }
+
+ // Start the service dispatcher if requested. This does not return until all services have terminated. If any
+ // global initialization is needed, it should be done before starting the service dispatcher, but only if it
+ // will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
+
+ if( start )
+ {
+ ServiceMain( argc, argv );
+ //ok = StartServiceCtrlDispatcher( gServiceDispatchTable );
+ //err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
+ //if( err != kNoErr )
+ //{
+ // ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err );
+ // goto exit;
+ //}
+ }
+ err = 0;
+
+exit:
+ dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err );
+ _CrtDumpMemoryLeaks();
+ return( (int) err );
+}
+
+//===========================================================================================================================
+// Usage
+//===========================================================================================================================
+
+static void Usage( void )
+{
+ fprintf( stderr, "\n" );
+ fprintf( stderr, "mdnsd 1.0d1\n" );
+ fprintf( stderr, "\n" );
+ fprintf( stderr, " <no args> Runs the service normally\n" );
+ fprintf( stderr, " -start Starts the service dispatcher after processing all other arguments\n" );
+ fprintf( stderr, " -server Runs the service directly as a server (for debugging)\n" );
+ fprintf( stderr, " -q Toggles Quiet Mode (no events or output)\n" );
+ fprintf( stderr, " -h[elp] Display Help/Usage\n" );
+ fprintf( stderr, "\n" );
+}
+
+//===========================================================================================================================
+// ConsoleControlHandler
+//===========================================================================================================================
+
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent )
+{
+ BOOL handled;
+ OSStatus err;
+
+ handled = FALSE;
+ switch( inControlEvent )
+ {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ err = ServiceSpecificStop();
+ require_noerr( err, exit );
+
+ handled = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+exit:
+ return( handled );
+}
+
+//===========================================================================================================================
+// InstallService
+//===========================================================================================================================
+
+static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath )
+{
+ OSStatus err;
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ok;
+ TCHAR fullPath[ MAX_PATH ];
+ TCHAR * namePtr;
+ DWORD size;
+
+ scm = NULL;
+ service = NULL;
+
+ // Get a full path to the executable since a relative path may have been specified.
+
+ size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr );
+ err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
+ require_noerr( err, exit );
+
+ // Create the service and start it.
+
+ scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
+ require_noerr( err, exit );
+
+ service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies,
+ NULL, NULL );
+ err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr );
+ require_noerr( err, exit );
+
+ err = SetServiceParameters();
+ check_noerr( err );
+
+ if( inDescription )
+ {
+ err = SetServiceInfo( scm, inName, inDescription );
+ check_noerr( err );
+ }
+
+ ok = StartService( service, 0, NULL );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
+ require_noerr( err, exit );
+
+ ReportStatus( EVENTLOG_SUCCESS, "installed service\n" );
+ err = kNoErr;
+
+exit:
+ if( service )
+ {
+ CloseServiceHandle( service );
+ }
+ if( scm )
+ {
+ CloseServiceHandle( scm );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// RemoveService
+//===========================================================================================================================
+
+static OSStatus RemoveService( LPCTSTR inName )
+{
+ OSStatus err;
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ok;
+ SERVICE_STATUS status;
+
+ scm = NULL;
+ service = NULL;
+
+ // Open a connection to the service.
+
+ scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS );
+ err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
+ require_noerr( err, exit );
+
+ service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE );
+ err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
+ require_noerr( err, exit );
+
+ // Stop the service, if it is not already stopped, then delete it.
+
+ ok = QueryServiceStatus( service, &status );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
+ require_noerr( err, exit );
+
+ if( status.dwCurrentState != SERVICE_STOPPED )
+ {
+ ok = ControlService( service, SERVICE_CONTROL_STOP, &status );
+ check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
+ }
+
+ ok = DeleteService( service );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr );
+ require_noerr( err, exit );
+
+ ReportStatus( EVENTLOG_SUCCESS, "Removed service\n" );
+ err = ERROR_SUCCESS;
+
+exit:
+ if( service )
+ {
+ CloseServiceHandle( service );
+ }
+ if( scm )
+ {
+ CloseServiceHandle( scm );
+ }
+ return( err );
+}
+
+
+
+//===========================================================================================================================
+// SetServiceParameters
+//===========================================================================================================================
+
+static OSStatus SetServiceParameters()
+{
+ DWORD value;
+ DWORD valueLen = sizeof(DWORD);
+ DWORD type;
+ OSStatus err;
+ HKEY key;
+
+ key = NULL;
+
+ //
+ // Add/Open Parameters section under service entry in registry
+ //
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+ require_noerr( err, exit );
+
+ //
+ // If the value isn't already there, then we create it
+ //
+ err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
+
+ if (err != ERROR_SUCCESS)
+ {
+ value = 1;
+
+ err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) );
+ require_noerr( err, exit );
+ }
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ return( err );
+}
+
+
+
+//===========================================================================================================================
+// GetServiceParameters
+//===========================================================================================================================
+
+static OSStatus GetServiceParameters()
+{
+ DWORD value;
+ DWORD valueLen;
+ DWORD type;
+ OSStatus err;
+ HKEY key;
+
+ key = NULL;
+
+ //
+ // Add/Open Parameters section under service entry in registry
+ //
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+ require_noerr( err, exit );
+
+ valueLen = sizeof(DWORD);
+ err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
+ if (err == ERROR_SUCCESS)
+ {
+ gServiceManageLLRouting = (value) ? true : false;
+ }
+
+ valueLen = sizeof(DWORD);
+ err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen);
+ if (err == ERROR_SUCCESS)
+ {
+ gServiceCacheEntryCount = value;
+ }
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ return( err );
+}
+
+
+//===========================================================================================================================
+// CheckFirewall
+//===========================================================================================================================
+
+static OSStatus CheckFirewall()
+{
+ DWORD value;
+ DWORD valueLen;
+ DWORD type;
+ ENUM_SERVICE_STATUS * lpService = NULL;
+ SC_HANDLE sc = NULL;
+ HKEY key = NULL;
+ BOOL ok;
+ DWORD bytesNeeded = 0;
+ DWORD srvCount;
+ DWORD resumeHandle = 0;
+ DWORD srvType;
+ DWORD srvState;
+ DWORD dwBytes = 0;
+ DWORD i;
+ BOOL isRunning = FALSE;
+ OSStatus err = kUnknownErr;
+
+ // Check to see if the firewall service is running. If it isn't, then
+ // we want to return immediately
+
+ sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
+ err = translate_errno( sc, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ srvType = SERVICE_WIN32;
+ srvState = SERVICE_STATE_ALL;
+
+ for ( ;; )
+ {
+ // Call EnumServicesStatus using the handle returned by OpenSCManager
+
+ ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
+
+ if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
+ {
+ break;
+ }
+
+ if ( lpService )
+ {
+ free( lpService );
+ }
+
+ dwBytes = bytesNeeded;
+
+ lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
+ require_action( lpService, exit, err = mStatus_NoMemoryErr );
+ }
+
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ for ( i = 0; i < srvCount; i++ )
+ {
+ if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 )
+ {
+ if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING )
+ {
+ isRunning = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ require_action( isRunning, exit, err = kUnknownErr );
+
+ // Check to see if we've managed the firewall.
+ // This package might have been installed, then
+ // the OS was upgraded to SP2 or above. If that's
+ // the case, then we need to manipulate the firewall
+ // so networking works correctly.
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+ require_noerr( err, exit );
+
+ valueLen = sizeof(DWORD);
+ err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen);
+
+ if ((err != ERROR_SUCCESS) || (value == 0))
+ {
+ wchar_t fullPath[ MAX_PATH ];
+ DWORD size;
+
+ // Get a full path to the executable
+
+ size = GetModuleFileNameW( NULL, fullPath, MAX_PATH );
+ err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
+ require_noerr( err, exit );
+
+ err = mDNSAddToFirewall(fullPath, kServiceFirewallName);
+ require_noerr( err, exit );
+
+ value = 1;
+ err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) );
+ require_noerr( err, exit );
+ }
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ if ( lpService )
+ {
+ free( lpService );
+ }
+
+ if ( sc )
+ {
+ CloseServiceHandle ( sc );
+ }
+
+ return( err );
+}
+
+
+
+//===========================================================================================================================
+// SetServiceInfo
+//===========================================================================================================================
+
+static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription )
+{
+ OSStatus err;
+ SC_LOCK lock;
+ SC_HANDLE service;
+ SERVICE_DESCRIPTION description;
+ SERVICE_FAILURE_ACTIONS actions;
+ SC_ACTION action;
+ BOOL ok;
+
+ check( inServiceName );
+ check( inDescription );
+
+ lock = NULL;
+ service = NULL;
+
+ // Open the database (if not provided) and lock it to prevent other access while re-configuring.
+
+ if( !inSCM )
+ {
+ inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr );
+ require_noerr( err, exit );
+ }
+
+ lock = LockServiceDatabase( inSCM );
+ err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr );
+ require_noerr( err, exit );
+
+ // Open a handle to the service.
+
+ service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START );
+ err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
+ require_noerr( err, exit );
+
+ // Change the description.
+
+ description.lpDescription = (LPTSTR) inDescription;
+ ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
+ require_noerr( err, exit );
+
+ actions.dwResetPeriod = INFINITE;
+ actions.lpRebootMsg = NULL;
+ actions.lpCommand = NULL;
+ actions.cActions = 1;
+ actions.lpsaActions = &action;
+ action.Delay = 500;
+ action.Type = SC_ACTION_RESTART;
+
+ ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
+ require_noerr( err, exit );
+
+ err = ERROR_SUCCESS;
+
+exit:
+ // Close the service and release the lock.
+
+ if( service )
+ {
+ CloseServiceHandle( service );
+ }
+ if( lock )
+ {
+ UnlockServiceDatabase( lock );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// ReportStatus
+//===========================================================================================================================
+
+static void ReportStatus( int inType, const char *inFormat, ... )
+{
+ if( !gServiceQuietMode )
+ {
+ va_list args;
+
+ va_start( args, inFormat );
+ if( gServiceEventSource )
+ {
+ char s[ 1024 ];
+ BOOL ok;
+ const char * array[ 1 ];
+
+ vsprintf( s, inFormat, args );
+ array[ 0 ] = s;
+ ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL );
+ check_translated_errno( ok, GetLastError(), kUnknownErr );
+ }
+ else
+ {
+ int n;
+
+ n = vfprintf( stderr, inFormat, args );
+ check( n >= 0 );
+ }
+ va_end( args );
+ }
+}
+
+//===========================================================================================================================
+// RunDirect
+//===========================================================================================================================
+
+int RunDirect( int argc, LPTSTR argv[] )
+{
+ OSStatus err;
+ BOOL initialized;
+ BOOL ok;
+
+ initialized = FALSE;
+
+ err = SetupServiceEvents();
+ require_noerr( err, exit );
+
+ // Install a Console Control Handler to handle things like control-c signals.
+
+ ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ err = ServiceSpecificInitialize( argc, argv );
+ require_noerr( err, exit );
+ initialized = TRUE;
+
+ // Run the service. This does not return until the service quits or is stopped.
+
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Running service directly\n" );
+
+ err = ServiceSpecificRun( argc, argv );
+ require_noerr( err, exit );
+
+ // Clean up.
+
+exit:
+ if( initialized )
+ {
+ ServiceSpecificFinalize( argc, argv );
+ }
+
+ TearDownServiceEvents();
+
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// ServiceMain
+//===========================================================================================================================
+
+static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] )
+{
+ OSStatus err;
+ BOOL ok;
+
+ err = SetupServiceEvents();
+ require_noerr( err, exit );
+
+ err = ServiceSetupEventLogging();
+ check_noerr( err );
+
+ //err = GetServiceParameters();
+ //check_noerr( err );
+
+ // Initialize the service status and register the service control handler with the name of the service.
+
+ gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
+ gServiceStatus.dwCurrentState = 0;
+ gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_POWEREVENT;
+ gServiceStatus.dwWin32ExitCode = NO_ERROR;
+ gServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
+ gServiceStatus.dwCheckPoint = 0;
+ gServiceStatus.dwWaitHint = 0;
+
+ //gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL );
+ //err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr );
+ //require_noerr( err, exit );
+
+ // Mark the service as starting.
+
+ gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ gServiceStatus.dwCheckPoint = 0;
+ gServiceStatus.dwWaitHint = 5000; // 5 seconds
+ //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+ //check_translated_errno( ok, GetLastError(), kParamErr );
+
+ // Run the service. This does not return until the service quits or is stopped.
+
+ err = ServiceRun( (int) argc, argv );
+ if( err != kNoErr )
+ {
+ gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+ gServiceStatus.dwServiceSpecificExitCode = (DWORD) err;
+ }
+
+ // Service-specific work is done so mark the service as stopped.
+
+ gServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+ //check_translated_errno( ok, GetLastError(), kParamErr );
+
+ // Note: The service status handle should not be closed according to Microsoft documentation.
+
+exit:
+
+ if( gServiceEventSource )
+ {
+ ok = DeregisterEventSource( gServiceEventSource );
+ check_translated_errno( ok, GetLastError(), kUnknownErr );
+ gServiceEventSource = NULL;
+ }
+
+ TearDownServiceEvents();
+}
+
+//===========================================================================================================================
+// ServiceSetupEventLogging
+//===========================================================================================================================
+
+static OSStatus ServiceSetupEventLogging( void )
+{
+ OSStatus err;
+ HKEY key;
+ LPCTSTR s;
+ DWORD typesSupported;
+ TCHAR path[ MAX_PATH ];
+ DWORD n;
+
+ key = NULL;
+
+ // Add/Open source name as a sub-key under the Application key in the EventLog registry key.
+
+ s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName;
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key );
+ require_noerr( err, exit );
+
+ // Add the name to the EventMessageFile subkey.
+
+ path[ 0 ] = '\0';
+ GetModuleFileName( NULL, path, MAX_PATH );
+ n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) );
+ err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+ require_noerr( err, exit );
+
+ // Set the supported event types in the TypesSupported subkey.
+
+ typesSupported = 0
+ | EVENTLOG_SUCCESS
+ | EVENTLOG_ERROR_TYPE
+ | EVENTLOG_WARNING_TYPE
+ | EVENTLOG_INFORMATION_TYPE
+ | EVENTLOG_AUDIT_SUCCESS
+ | EVENTLOG_AUDIT_FAILURE;
+ err = RegSetValueEx( key, TEXT("TypesSupported"), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+ require_noerr( err, exit );
+
+ // Set up the event source.
+
+ gServiceEventSource = RegisterEventSource( NULL, kServiceName );
+ err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr );
+ require_noerr( err, exit );
+
+exit:
+ if( key )
+ {
+ RegCloseKey( key );
+ }
+ return( err );
+}
+
+
+//===========================================================================================================================
+// ServiceControlHandler
+//===========================================================================================================================
+
+static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext )
+{
+ BOOL setStatus;
+ OSStatus err;
+ BOOL ok;
+
+ DEBUG_UNUSED( inEventData );
+ DEBUG_UNUSED( inContext );
+
+ setStatus = TRUE;
+ switch( inControl )
+ {
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP|SERVICE_CONTROL_SHUTDOWN\n" );
+
+ ServiceStop();
+ setStatus = FALSE;
+ break;
+
+ case SERVICE_CONTROL_POWEREVENT:
+
+ if (inEventType == PBT_APMSUSPEND)
+ {
+ dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMSUSPEND\n" );
+
+ if ( gPowerSuspendEvent )
+ {
+ ok = SetEvent( gPowerSuspendEvent );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ check_noerr( err );
+
+ switch ( WaitForSingleObject( gPowerSuspendAckEvent, 5 * 1000 ) )
+ {
+ case WAIT_OBJECT_0:
+ {
+ // No error
+ }
+ break;
+
+ case WAIT_TIMEOUT:
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "Timed out waiting for acknowledgement of machine sleep\n" );
+ ReportStatus( EVENTLOG_ERROR_TYPE, "Timed out waiting for acknowledgement of machine sleep" );
+ }
+ break;
+
+ default:
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
+ ReportStatus( EVENTLOG_ERROR_TYPE, "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
+ }
+ break;
+ }
+ }
+ }
+ else if (inEventType == PBT_APMRESUMESUSPEND)
+ {
+ dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" );
+
+ if ( gPowerResumeEvent )
+ {
+ ok = SetEvent( gPowerResumeEvent );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ check_noerr( err );
+ }
+ }
+
+ break;
+
+ default:
+ dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl );
+ break;
+ }
+
+ if( setStatus && gServiceStatusHandle )
+ {
+ //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+ //check_translated_errno( ok, GetLastError(), kUnknownErr );
+ }
+
+ return NO_ERROR;
+}
+
+//===========================================================================================================================
+// ServiceRun
+//===========================================================================================================================
+
+static OSStatus ServiceRun( int argc, LPTSTR argv[] )
+{
+ OSStatus err;
+ BOOL initialized;
+
+ DEBUG_UNUSED( argc );
+ DEBUG_UNUSED( argv );
+
+ initialized = FALSE;
+
+ // <rdar://problem/5727548> Make the service as running before we call ServiceSpecificInitialize. We've
+ // had reports that some machines with McAfee firewall installed cause a problem with iTunes installation.
+ // We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a
+ // simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock
+ // any installers that are waiting for our state to change.
+
+ gServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+ //check_translated_errno( ok, GetLastError(), kParamErr );
+
+ // Initialize the service-specific stuff
+
+ while ( 1 )
+ {
+ DWORD ret;
+
+ //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initializing" );
+
+ err = ServiceSpecificInitialize( argc, argv );
+
+ if ( !err )
+ {
+ //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialized" );
+ break;
+ }
+
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialization failed with err %d. Waiting %d seconds to retry...", err, kWaitToRetry );
+
+ ret = WaitForSingleObject( gStopEvent, 1000 * kWaitToRetry );
+
+ if ( ret == WAIT_OBJECT_0 )
+ {
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a stop event" );
+ goto exit;
+ }
+ else if ( ret == WAIT_OBJECT_0 + 1 )
+ {
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power suspend event" );
+ }
+ else if ( ret == WAIT_OBJECT_0 + 2 )
+ {
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power resume event" );
+ }
+ else if ( ret != WAIT_TIMEOUT )
+ {
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received an error in WaitForSingleObject() : %d, %d", ret, GetLastError() );
+ goto exit;
+ }
+ }
+
+ initialized = TRUE;
+
+ err = CheckFirewall();
+ check_noerr( err );
+
+ if ( err )
+ {
+ gRetryFirewall = TRUE;
+ }
+
+ if (gJustCheckFirewall)
+ goto exit;
+ // Run the service-specific stuff. This does not return until the service quits or is stopped.
+
+ //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service started\n" );
+
+ err = ServiceSpecificRun( argc, argv );
+ require_noerr( err, exit );
+
+exit:
+
+ // Service stopped. Clean up and we're done.
+
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err );
+
+ if( initialized )
+ {
+ ServiceSpecificFinalize( argc, argv );
+ }
+
+ return( err );
+}
+
+//===========================================================================================================================
+// ServiceStop
+//===========================================================================================================================
+
+static void ServiceStop( void )
+{
+ OSStatus err;
+
+ // Signal the event to cause the service to exit.
+
+ if( gServiceStatusHandle )
+ {
+ gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+ //check_translated_errno( ok, GetLastError(), kParamErr );
+ }
+
+ err = ServiceSpecificStop();
+ check_noerr( err );
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark == Service Specific ==
+#endif
+
+//===========================================================================================================================
+// ServiceSpecificInitialize
+//===========================================================================================================================
+
+static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] )
+{
+ OSStatus err;
+
+ DEBUG_UNUSED( argc );
+ DEBUG_UNUSED( argv );
+
+ mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord);
+ mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage);
+
+ gPlatformStorage.reportStatusFunc = ReportStatus;
+
+ err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext);
+ require_noerr( err, exit);
+
+ err = SetupNotifications();
+ check_noerr( err );
+
+ err = udsserver_init(mDNSNULL, 0);
+ require_noerr( err, exit);
+
+ SetLLRoute( &gMDNSRecord );
+
+exit:
+ if( err != kNoErr )
+ {
+ ServiceSpecificFinalize( argc, argv );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// ServiceSpecificRun
+//===========================================================================================================================
+
+static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] )
+{
+ mDNSBool done = mDNSfalse;
+ mStatus err = mStatus_NoError;
+
+ DEBUG_UNUSED( argc );
+ DEBUG_UNUSED( argv );
+
+ err = SetupInterfaceList( &gMDNSRecord );
+ check( !err );
+
+ err = uDNS_SetupDNSConfig( &gMDNSRecord );
+ check( !err );
+
+ while( !done )
+ {
+ static mDNSs32 RepeatedBusy = 0;
+ mDNSs32 nextTimerEvent;
+ mStatus err;
+
+ // Give the mDNS core a chance to do its work and determine next event time.
+
+ nextTimerEvent = udsserver_idle( mDNS_Execute( &gMDNSRecord ) - mDNS_TimeNow( &gMDNSRecord ) );
+
+ if ( nextTimerEvent < 0) nextTimerEvent = 0;
+ else if ( nextTimerEvent > (0x7FFFFFFF / 1000)) nextTimerEvent = 0x7FFFFFFF / mDNSPlatformOneSecond;
+ else nextTimerEvent = ( nextTimerEvent * 1000) / mDNSPlatformOneSecond;
+
+ // Debugging sanity check, to guard against CPU spins
+
+ if ( nextTimerEvent > 0 )
+ {
+ RepeatedBusy = 0;
+ }
+ else
+ {
+ nextTimerEvent = 1;
+
+ if ( ++RepeatedBusy >= mDNSPlatformOneSecond )
+ {
+ ShowTaskSchedulingError( &gMDNSRecord );
+ RepeatedBusy = 0;
+ }
+ }
+
+ if ( gMDNSRecord.ShutdownTime )
+ {
+ mDNSs32 now = mDNS_TimeNow( &gMDNSRecord );
+
+ if ( mDNS_ExitNow( &gMDNSRecord, now ) )
+ {
+ mDNS_FinalExit( &gMDNSRecord );
+ done = TRUE;
+ break;
+ }
+
+ if ( nextTimerEvent - gMDNSRecord.ShutdownTime >= 0 )
+ {
+ nextTimerEvent = gMDNSRecord.ShutdownTime;
+ }
+ }
+
+ err = mDNSPoll( nextTimerEvent );
+
+ if ( err )
+ {
+ Sleep( 3 * 1000 );
+
+ err = SetupInterfaceList( &gMDNSRecord );
+ check( !err );
+
+ err = uDNS_SetupDNSConfig( &gMDNSRecord );
+ check( !err );
+
+ break;
+ }
+ }
+
+ return ( err );
+}
+
+
+//===========================================================================================================================
+// ServiceSpecificStop
+//===========================================================================================================================
+
+static OSStatus ServiceSpecificStop( void )
+{
+ OSStatus err;
+ BOOL ok;
+
+ ok = SetEvent(gStopEvent);
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+exit:
+
+ return( err );
+}
+
+//===========================================================================================================================
+// ServiceSpecificFinalize
+//===========================================================================================================================
+
+static void ServiceSpecificFinalize( int argc, LPTSTR argv[] )
+{
+ DEBUG_UNUSED( argc );
+ DEBUG_UNUSED( argv );
+
+ //
+ // clean up the notifications
+ //
+ TearDownNotifications();
+
+ //
+ // clean up loaded library
+ //
+
+ if( gIPHelperLibraryInstance )
+ {
+ gGetIpInterfaceEntryFunctionPtr = NULL;
+
+ FreeLibrary( gIPHelperLibraryInstance );
+ gIPHelperLibraryInstance = NULL;
+ }
+}
+
+
+//===========================================================================================================================
+// SetupServiceEvents
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupServiceEvents()
+{
+ mStatus err;
+
+ // Stop Event
+
+ gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gStopEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err )
+ {
+ TearDownServiceEvents();
+ }
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// TearDownServiceNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownServiceEvents()
+{
+ if ( gStopEvent )
+ {
+ CloseHandle( gStopEvent );
+ gStopEvent = NULL;
+ }
+
+ return mStatus_NoError;
+}
+
+
+//===========================================================================================================================
+// SetupNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupNotifications()
+{
+ mStatus err;
+ SocketRef sock;
+ unsigned long param;
+ int inBuffer;
+ int outBuffer;
+ DWORD outSize;
+
+ require_action( gStopEvent, exit, err = kUnknownErr );
+ err = mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL );
+ require_noerr( err, exit );
+
+ // Power Suspend
+
+ gPowerSuspendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gPowerSuspendEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gPowerSuspendEvent, PowerSuspendNotification, NULL );
+ require_noerr( err, exit );
+
+ // Power Suspend Ack
+
+ gPowerSuspendAckEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ err = translate_errno( gPowerSuspendAckEvent, ( mStatus ) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Power Resume
+
+ gPowerResumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gPowerResumeEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gPowerResumeEvent, PowerResumeNotification, NULL );
+ require_noerr( err, exit );
+
+ // Register to listen for address list changes.
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+ gInterfaceListChangedSocket = sock;
+
+ // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event
+ // when a change to the interface list is detected.
+
+ param = 1;
+ err = ioctlsocket( sock, FIONBIO, &param );
+ err = translate_errno( err == 0, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ inBuffer = 0;
+ outBuffer = 0;
+ err = WSAIoctl( sock, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
+ if( err < 0 )
+ {
+ check( errno_compat() == WSAEWOULDBLOCK );
+ }
+
+ err = mDNSPollRegisterSocket( sock, FD_ADDRESS_LIST_CHANGE, InterfaceListNotification, NULL );
+ require_noerr( err, exit );
+
+ gDescChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ err = translate_errno( gDescChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"), 0, KEY_READ, &gDescKey);
+ check_translated_errno( err == 0, errno_compat(), kNameErr );
+
+ if ( gDescKey != NULL )
+ {
+ err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
+ require_noerr( err, exit );
+ }
+
+ err = mDNSPollRegisterEvent( gDescChangedEvent, ComputerDescriptionNotification, NULL );
+ require_noerr( err, exit );
+
+ // This will catch all changes to tcp/ip networking, including changes to the domain search list
+
+ gTcpipChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gTcpipChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &gTcpipKey );
+ require_noerr( err, exit );
+ err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE);
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gTcpipChangedEvent, TCPChangedNotification, NULL );
+ require_noerr( err, exit );
+
+ // This will catch all changes to ddns configuration
+
+ gDdnsChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gDdnsChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &gDdnsKey );
+ require_noerr( err, exit );
+ err = RegNotifyChangeKeyValue( gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gDdnsChangedEvent, DDNSChangedNotification, NULL );
+ require_noerr( err, exit );
+
+ // This will catch all changes to file sharing
+
+ gFileSharingChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ err = translate_errno( gFileSharingChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\Shares"), &gFileSharingKey );
+
+ // Just to make sure that initialization doesn't fail on some old OS
+ // that doesn't have this key, we'll only add the notification if
+ // the key exists.
+
+ if ( !err )
+ {
+ err = RegNotifyChangeKeyValue( gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gFileSharingChangedEvent, FileSharingChangedNotification, NULL );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ err = mStatus_NoError;
+ }
+
+ // This will catch changes to the Windows firewall
+
+ gFirewallChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ err = translate_errno( gFirewallChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Just to make sure that initialization doesn't fail on some old OS
+ // that doesn't have this key, we'll only add the notification if
+ // the key exists.
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"), &gFirewallKey );
+
+ if ( !err )
+ {
+ err = RegNotifyChangeKeyValue( gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gFirewallChangedEvent, FirewallChangedNotification, NULL );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ err = mStatus_NoError;
+ }
+
+ // This will catch all changes to advertised services configuration
+
+ gAdvertisedServicesChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ err = translate_errno( gAdvertisedServicesChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\Services"), &gAdvertisedServicesKey );
+ require_noerr( err, exit );
+ err = RegNotifyChangeKeyValue( gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gAdvertisedServicesChangedEvent, AdvertisedServicesChangedNotification, NULL );
+ require_noerr( err, exit );
+
+ // SPSWakeup timer
+
+ gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL );
+ err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gSPSWakeupEvent, SPSWakeupNotification, NULL );
+ require_noerr( err, exit );
+
+ // SPSSleep timer
+
+ gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL );
+ err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ err = mDNSPollRegisterEvent( gSPSSleepEvent, SPSSleepNotification, NULL );
+ require_noerr( err, exit );
+
+exit:
+ if( err )
+ {
+ TearDownNotifications();
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownNotifications()
+{
+ if( IsValidSocket( gInterfaceListChangedSocket ) )
+ {
+ mDNSPollUnregisterSocket( gInterfaceListChangedSocket );
+
+ close_compat( gInterfaceListChangedSocket );
+ gInterfaceListChangedSocket = kInvalidSocketRef;
+ }
+
+ if ( gDescChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gDescChangedEvent );
+ CloseHandle( gDescChangedEvent );
+ gDescChangedEvent = NULL;
+ }
+
+ if ( gDescKey != NULL )
+ {
+ RegCloseKey( gDescKey );
+ gDescKey = NULL;
+ }
+
+ if ( gTcpipChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gTcpipChangedEvent );
+ CloseHandle( gTcpipChangedEvent );
+ gTcpipChangedEvent = NULL;
+ }
+
+ if ( gDdnsChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gDdnsChangedEvent );
+ CloseHandle( gDdnsChangedEvent );
+ gDdnsChangedEvent = NULL;
+ }
+
+ if ( gDdnsKey != NULL )
+ {
+ RegCloseKey( gDdnsKey );
+ gDdnsKey = NULL;
+ }
+
+ if ( gFileSharingChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gFileSharingChangedEvent );
+ CloseHandle( gFileSharingChangedEvent );
+ gFileSharingChangedEvent = NULL;
+ }
+
+ if ( gFileSharingKey != NULL )
+ {
+ RegCloseKey( gFileSharingKey );
+ gFileSharingKey = NULL;
+ }
+
+ if ( gFirewallChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gFirewallChangedEvent );
+ CloseHandle( gFirewallChangedEvent );
+ gFirewallChangedEvent = NULL;
+ }
+
+ if ( gFirewallKey != NULL )
+ {
+ RegCloseKey( gFirewallKey );
+ gFirewallKey = NULL;
+ }
+
+ if ( gAdvertisedServicesChangedEvent != NULL )
+ {
+ mDNSPollUnregisterEvent( gAdvertisedServicesChangedEvent );
+ CloseHandle( gAdvertisedServicesChangedEvent );
+ gAdvertisedServicesChangedEvent = NULL;
+ }
+
+ if ( gAdvertisedServicesKey != NULL )
+ {
+ RegCloseKey( gAdvertisedServicesKey );
+ gAdvertisedServicesKey = NULL;
+ }
+
+ if ( gSPSWakeupEvent )
+ {
+ mDNSPollUnregisterEvent( gSPSWakeupEvent );
+ CloseHandle( gSPSWakeupEvent );
+ gSPSWakeupEvent = NULL;
+ }
+
+ if ( gSPSSleepEvent )
+ {
+ mDNSPollUnregisterEvent( gSPSSleepEvent );
+ CloseHandle( gSPSSleepEvent );
+ gSPSSleepEvent = NULL;
+ }
+
+ if ( gPowerResumeEvent )
+ {
+ mDNSPollUnregisterEvent( gPowerResumeEvent );
+ CloseHandle( gPowerResumeEvent );
+ gPowerResumeEvent = NULL;
+ }
+
+ if ( gPowerSuspendAckEvent )
+ {
+ CloseHandle( gPowerSuspendAckEvent );
+ gPowerSuspendAckEvent = NULL;
+ }
+
+ if ( gPowerSuspendEvent )
+ {
+ mDNSPollUnregisterEvent( gPowerSuspendEvent );
+ CloseHandle( gPowerSuspendEvent );
+ gPowerSuspendEvent = NULL;
+ }
+
+ if ( gStopEvent )
+ {
+ mDNSPollUnregisterEvent( gStopEvent );
+ }
+
+ return( mStatus_NoError );
+}
+
+
+mDNSlocal void CALLBACK
+StopNotification( HANDLE event, void *context )
+{
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" );
+ udsserver_exit();
+ mDNS_StartExit( &gMDNSRecord );
+}
+
+
+mDNSlocal void CALLBACK
+PowerSuspendNotification( HANDLE event, void * context )
+{
+ LARGE_INTEGER timeout;
+ BOOL ok;
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "PowerSuspendNotification\n" );
+
+ gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout );
+
+ if ( gMDNSRecord.SystemWakeOnLANEnabled )
+ {
+ ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE );
+ check( ok );
+ }
+
+ mDNSCoreMachineSleep(&gMDNSRecord, TRUE);
+
+ ok = SetEvent( gPowerSuspendAckEvent );
+
+ if ( !ok )
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
+ ReportStatus( EVENTLOG_ERROR_TYPE, "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+PowerResumeNotification( HANDLE event, void * context )
+{
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "PowerResumeNotification\n" );
+
+ if ( gSPSWakeupEvent )
+ {
+ CancelWaitableTimer( gSPSWakeupEvent );
+ }
+
+ if ( gSPSSleepEvent )
+ {
+ CancelWaitableTimer( gSPSSleepEvent );
+ }
+
+ mDNSCoreMachineSleep(&gMDNSRecord, FALSE);
+}
+
+
+
+mDNSlocal void CALLBACK
+InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context )
+{
+ int inBuffer;
+ int outBuffer;
+ DWORD outSize;
+ int err;
+
+ DEBUG_UNUSED( socket );
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ // It would be nice to come up with a more elegant solution to this, but it seems that
+ // GetAdaptersAddresses doesn't always stay in sync after network changed events. So as
+ // as a simple workaround, we'll pause for a couple of seconds before processing the change.
+
+ // We arrived at 2 secs by trial and error. We could reproduce the problem after sleeping
+ // for 500 msec and 750 msec, but couldn't after sleeping for 1 sec. We added another
+ // second on top of that to account for machine load or some other exigency.
+
+ Sleep( 2000 );
+
+ // Interface list changed event. Break out of the inner loop to re-setup the wait list.
+
+ InterfaceListDidChange( &gMDNSRecord );
+
+ // reset the event handler
+ inBuffer = 0;
+ outBuffer = 0;
+ err = WSAIoctl( gInterfaceListChangedSocket, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
+ if( err < 0 )
+ {
+ check( errno_compat() == WSAEWOULDBLOCK );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+ComputerDescriptionNotification( HANDLE event, void *context )
+{
+ // The computer description might have changed
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ ComputerDescriptionDidChange( &gMDNSRecord );
+ udsserver_handle_configchange( &gMDNSRecord );
+
+ // and reset the event handler
+ if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) )
+ {
+ int err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+TCPChangedNotification( HANDLE event, void *context )
+{
+ // The TCP/IP might have changed
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ TCPIPConfigDidChange( &gMDNSRecord );
+ udsserver_handle_configchange( &gMDNSRecord );
+
+ // and reset the event handler
+
+ if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) )
+ {
+ int err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE );
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+DDNSChangedNotification( HANDLE event, void *context )
+{
+ // The DynDNS config might have changed
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ DynDNSConfigDidChange( &gMDNSRecord );
+ udsserver_handle_configchange( &gMDNSRecord );
+
+ // and reset the event handler
+
+ if ((gDdnsKey != NULL) && (gDdnsChangedEvent))
+ {
+ int err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+FileSharingChangedNotification( HANDLE event, void *context )
+{
+ // File sharing changed
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ FileSharingDidChange( &gMDNSRecord );
+
+ // and reset the event handler
+
+ if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent))
+ {
+ int err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+FirewallChangedNotification( HANDLE event, void *context )
+{
+ // Firewall configuration changed
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ FirewallDidChange( &gMDNSRecord );
+
+ // and reset the event handler
+
+ if ((gFirewallKey != NULL) && (gFirewallChangedEvent))
+ {
+ int err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+AdvertisedServicesChangedNotification( HANDLE event, void *context )
+{
+ // Ultimately we'll want to manage multiple services, but right now the only service
+ // we'll be managing is SMB.
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ FileSharingDidChange( &gMDNSRecord );
+
+ // and reset the event handler
+
+ if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) )
+ {
+ int err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
+ check_noerr( err );
+ }
+}
+
+
+mDNSlocal void CALLBACK
+SPSWakeupNotification( HANDLE event, void *context )
+{
+ LARGE_INTEGER timeout;
+
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" );
+
+ timeout.QuadPart = kSPSMaintenanceWakePeriod;
+ timeout.QuadPart *= kSecondsTo100NSUnits;
+
+ SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE );
+}
+
+
+mDNSlocal void CALLBACK
+SPSSleepNotification( HANDLE event, void *context )
+{
+ DEBUG_UNUSED( event );
+ DEBUG_UNUSED( context );
+
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "Returning to sleep after maintenance wake" );
+
+ // Calling SetSuspendState() doesn't invoke our sleep handlers, so we'll
+ // call HandlePowerSuspend() explicity. This will reset the
+ // maintenance wake timers.
+
+ PowerSuspendNotification( gPowerSuspendEvent, NULL );
+ SetSuspendState( FALSE, FALSE, FALSE );
+}
+
+
+//===========================================================================================================================
+// CoreCallback
+//===========================================================================================================================
+
+static void
+CoreCallback(mDNS * const inMDNS, mStatus status)
+{
+ if (status == mStatus_ConfigChanged)
+ {
+ SetLLRoute( inMDNS );
+ }
+}
+
+
+//===========================================================================================================================
+// UDSAcceptNotification
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
+{
+ ( void ) sock;
+ ( void ) event;
+ ( void ) context;
+
+ if ( gUDSCallback )
+ {
+ gUDSCallback( ( int ) gUDSSocket, 0, context );
+ }
+}
+
+
+//===========================================================================================================================
+// UDSReadNotification
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
+{
+ TCPSocket *tcpSock = ( TCPSocket* ) context;
+
+ ( void ) sock;
+ ( void ) event;
+
+ if ( tcpSock )
+ {
+ tcpSock->userCallback( ( int ) tcpSock->fd, 0, tcpSock->userContext );
+ }
+}
+
+
+//===========================================================================================================================
+// udsSupportAddFDToEventLoop
+//===========================================================================================================================
+
+mStatus
+udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
+{
+ mStatus err = mStatus_NoError;
+
+ // We are using some knowledge of what is being passed to us here. If the fd is a listen socket,
+ // then the "context" parameter is NULL. If it is an actual read/write socket, then the "context"
+ // parameter is not null.
+
+ if ( context )
+ {
+ TCPSocket * sock;
+
+ sock = malloc( sizeof( TCPSocket ) );
+ require_action( sock, exit, err = mStatus_NoMemoryErr );
+ mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
+
+ sock->fd = (SOCKET) fd;
+ sock->userCallback = callback;
+ sock->userContext = context;
+ sock->m = &gMDNSRecord;
+
+ *platform_data = sock;
+
+ err = mDNSPollRegisterSocket( sock->fd, FD_READ | FD_CLOSE, UDSReadNotification, sock );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ gUDSSocket = fd;
+ gUDSCallback = callback;
+
+ err = mDNSPollRegisterSocket( gUDSSocket, FD_ACCEPT | FD_CLOSE, UDSAcceptNotification, NULL );
+ require_noerr( err, exit );
+ }
+
+exit:
+
+ return err;
+}
+
+
+int
+udsSupportReadFD( SocketRef fd, char *buf, int len, int flags, void *platform_data )
+{
+ TCPSocket * sock;
+ mDNSBool closed;
+ int ret;
+
+ ( void ) flags;
+
+ sock = ( TCPSocket* ) platform_data;
+ require_action( sock, exit, ret = -1 );
+ require_action( sock->fd == fd, exit, ret = -1 );
+
+ ret = mDNSPlatformReadTCP( sock, buf, len, &closed );
+
+ if ( closed )
+ {
+ ret = 0;
+ }
+ else if ( !ret && ( WSAGetLastError() == WSAEWOULDBLOCK ) )
+ {
+ // mDNSPlatformReadTCP will return 0 if it gets WSAEWOULDBLOCK, but
+ // that caller of this routine interprets that as close connection.
+ // We'll fix that by returning -1 in that case.
+
+ ret = -1;
+ }
+
+exit:
+
+ return ret;
+}
+
+
+mStatus
+udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data) // Note: This also CLOSES the socket
+{
+ mStatus err = kNoErr;
+
+ mDNSPollUnregisterSocket( fd );
+
+ if ( platform_data != NULL )
+ {
+ TCPSocket * sock;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" );
+ sock = ( TCPSocket* ) platform_data;
+ check( sock->fd == fd );
+ mDNSPlatformTCPCloseConnection( sock );
+ }
+
+ return err;
+}
+
+
+mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
+ {
+ (void)m;
+ (void)delay;
+ // No-op, for now
+ }
+
+
+//===========================================================================================================================
+// SystemWakeForNetworkAccess
+//===========================================================================================================================
+
+mDNSu8
+SystemWakeForNetworkAccess( LARGE_INTEGER * timeout )
+{
+ HKEY key = NULL;
+ DWORD dwSize;
+ DWORD enabled;
+ mDNSu8 ok;
+ SYSTEM_POWER_STATUS powerStatus;
+ time_t startTime;
+ time_t nextWakeupTime;
+ int delta;
+ DWORD err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "SystemWakeForNetworkAccess\n" );
+
+ // Make sure we have a timer
+
+ require_action( gSPSWakeupEvent != NULL, exit, ok = FALSE );
+ require_action( gSPSSleepEvent != NULL, exit, ok = FALSE );
+
+ // Make sure the user enabled bonjour sleep proxy client
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", &key );
+ require_action( !err, exit, ok = FALSE );
+ dwSize = sizeof( DWORD );
+ err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+ require_action( !err, exit, ok = FALSE );
+ require_action( enabled, exit, ok = FALSE );
+
+ // Make sure machine is on AC power
+
+ ok = ( mDNSu8 ) GetSystemPowerStatus( &powerStatus );
+ require_action( ok, exit, ok = FALSE );
+ require_action( powerStatus.ACLineStatus == AC_LINE_ONLINE, exit, ok = FALSE );
+
+ // Now make sure we have a network interface that does wake-on-lan
+
+ ok = ( mDNSu8 ) IsWOMPEnabled( &gMDNSRecord );
+ require_action( ok, exit, ok = FALSE );
+
+ // Now make sure we have advertised services. Doesn't make sense to
+ // enable sleep proxy if we have no multicast services that could
+ // potentially wake us up.
+
+ ok = ( mDNSu8 ) mDNSCoreHaveAdvertisedMulticastServices( &gMDNSRecord );
+ require_action( ok, exit, ok = FALSE );
+
+ // Calculate next wake up time
+
+ startTime = time( NULL ); // Seconds since midnight January 1, 1970
+ nextWakeupTime = startTime + ( 120 * 60 ); // 2 hours later
+
+ if ( gMDNSRecord.p->nextDHCPLeaseExpires < nextWakeupTime )
+ {
+ nextWakeupTime = gMDNSRecord.p->nextDHCPLeaseExpires;
+ }
+
+ // Finally calculate the next relative wakeup time
+
+ delta = ( int )( ( ( double )( nextWakeupTime - startTime ) ) * 0.9 );
+ ReportStatus( EVENTLOG_INFORMATION_TYPE, "enabling sleep proxy client with next maintenance wake in %d seconds", delta );
+
+ // Convert seconds to 100 nanosecond units expected by SetWaitableTimer
+
+ timeout->QuadPart = -delta;
+ timeout->QuadPart *= kSecondsTo100NSUnits;
+
+ ok = TRUE;
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ return ok;
+}
+
+
+//===========================================================================================================================
+// HaveRoute
+//===========================================================================================================================
+
+static bool
+HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric )
+{
+ PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
+ DWORD dwSize = 0;
+ BOOL bOrder = FALSE;
+ OSStatus err;
+ bool found = false;
+ unsigned long int i;
+
+ //
+ // Find out how big our buffer needs to be.
+ //
+ err = GetIpForwardTable(NULL, &dwSize, bOrder);
+ require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
+
+ //
+ // Allocate the memory for the table
+ //
+ pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
+ require_action( pIpForwardTable, exit, err = kNoMemoryErr );
+
+ //
+ // Now get the table.
+ //
+ err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
+ require_noerr( err, exit );
+
+ //
+ // Search for the row in the table we want.
+ //
+ for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
+ {
+ if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) )
+ {
+ memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) );
+ found = true;
+ break;
+ }
+ }
+
+exit:
+
+ if ( pIpForwardTable != NULL )
+ {
+ free(pIpForwardTable);
+ }
+
+ return found;
+}
+
+
+//===========================================================================================================================
+// IsValidAddress
+//===========================================================================================================================
+
+static bool
+IsValidAddress( const char * addr )
+{
+ return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false;
+}
+
+
+//===========================================================================================================================
+// GetAdditionalMetric
+//===========================================================================================================================
+
+static ULONG
+GetAdditionalMetric( DWORD ifIndex )
+{
+ ULONG metric = 0;
+
+ if( !gIPHelperLibraryInstance )
+ {
+ gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
+
+ gGetIpInterfaceEntryFunctionPtr =
+ (GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" );
+
+ if( !gGetIpInterfaceEntryFunctionPtr )
+ {
+ BOOL ok;
+
+ ok = FreeLibrary( gIPHelperLibraryInstance );
+ check_translated_errno( ok, GetLastError(), kUnknownErr );
+ gIPHelperLibraryInstance = NULL;
+ }
+ }
+
+ if ( gGetIpInterfaceEntryFunctionPtr )
+ {
+ MIB_IPINTERFACE_ROW row;
+ DWORD err;
+
+ ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) );
+ row.Family = AF_INET;
+ row.InterfaceIndex = ifIndex;
+ err = gGetIpInterfaceEntryFunctionPtr( &row );
+ require_noerr( err, exit );
+ metric = row.Metric + 256;
+ }
+
+exit:
+
+ return metric;
+}
+
+
+//===========================================================================================================================
+// SetLLRoute
+//===========================================================================================================================
+
+static OSStatus
+SetLLRoute( mDNS * const inMDNS )
+{
+ OSStatus err = kNoErr;
+
+ DEBUG_UNUSED( inMDNS );
+
+ //
+ // <rdar://problem/4096464> Don't call SetLLRoute on loopback
+ // <rdar://problem/6885843> Default route on Windows 7 breaks network connectivity
+ //
+ // Don't mess w/ the routing table on Vista and later OSes, as
+ // they have a permanent route to link-local addresses. Otherwise,
+ // set a route to link local addresses (169.254.0.0)
+ //
+ if ( ( inMDNS->p->osMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 )
+ {
+ DWORD ifIndex;
+ MIB_IPFORWARDROW rowExtant;
+ bool addRoute;
+ MIB_IPFORWARDROW row;
+
+ ZeroMemory(&row, sizeof(row));
+
+ err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop);
+ require_noerr( err, exit );
+ row.dwForwardDest = inet_addr(kLLNetworkAddr);
+ row.dwForwardIfIndex = ifIndex;
+ row.dwForwardMask = inet_addr(kLLNetworkAddrMask);
+ row.dwForwardType = 3;
+ row.dwForwardProto = MIB_IPPROTO_NETMGMT;
+ row.dwForwardAge = 0;
+ row.dwForwardPolicy = 0;
+ row.dwForwardMetric1 = 20 + GetAdditionalMetric( ifIndex );
+ row.dwForwardMetric2 = (DWORD) - 1;
+ row.dwForwardMetric3 = (DWORD) - 1;
+ row.dwForwardMetric4 = (DWORD) - 1;
+ row.dwForwardMetric5 = (DWORD) - 1;
+
+ addRoute = true;
+
+ //
+ // check to make sure we don't already have a route
+ //
+ if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) )
+ {
+ //
+ // set the age to 0 so that we can do a memcmp.
+ //
+ rowExtant.dwForwardAge = 0;
+
+ //
+ // check to see if this route is the same as our route
+ //
+ if (memcmp(&row, &rowExtant, sizeof(row)) != 0)
+ {
+ //
+ // if it isn't then delete this entry
+ //
+ DeleteIpForwardEntry(&rowExtant);
+ }
+ else
+ {
+ //
+ // else it is, so we don't want to create another route
+ //
+ addRoute = false;
+ }
+ }
+
+ if (addRoute && row.dwForwardNextHop)
+ {
+ err = CreateIpForwardEntry(&row);
+ check_noerr( err );
+ }
+ }
+
+exit:
+
+ return ( err );
+}
+
+
+//===========================================================================================================================
+// GetRouteDestination
+//===========================================================================================================================
+
+static OSStatus
+GetRouteDestination(DWORD * ifIndex, DWORD * address)
+{
+ struct in_addr ia;
+ IP_ADAPTER_INFO * pAdapterInfo = NULL;
+ IP_ADAPTER_INFO * pAdapter = NULL;
+ ULONG bufLen;
+ mDNSBool done = mDNSfalse;
+ OSStatus err;
+
+ //
+ // GetBestInterface will fail if there is no default gateway
+ // configured. If that happens, we will just take the first
+ // interface in the list. MSDN support says there is no surefire
+ // way to manually determine what the best interface might
+ // be for a particular network address.
+ //
+ ia.s_addr = inet_addr(kLLNetworkAddr);
+ err = GetBestInterface(*(IPAddr*) &ia, ifIndex);
+
+ if (err)
+ {
+ *ifIndex = 0;
+ }
+
+ //
+ // Make an initial call to GetAdaptersInfo to get
+ // the necessary size into the bufLen variable
+ //
+ err = GetAdaptersInfo( NULL, &bufLen);
+ require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr );
+
+ pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen );
+ require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+
+ err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+ require_noerr( err, exit );
+
+ pAdapter = pAdapterInfo;
+ err = kUnknownErr;
+
+ // <rdar://problem/3718122>
+ // <rdar://problem/5652098>
+ //
+ // Look for the Nortel VPN virtual interface, along with Juniper virtual interface.
+ //
+ // If these interfaces are active (i.e., has a non-zero IP Address),
+ // then we want to disable routing table modifications.
+
+ while (pAdapter)
+ {
+ if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) &&
+ ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) )
+ {
+ dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" );
+ goto exit;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+ while ( !done )
+ {
+ pAdapter = pAdapterInfo;
+ err = kUnknownErr;
+
+ while (pAdapter)
+ {
+ // If we don't have an interface selected, choose the first one that is of type ethernet and
+ // has a valid IP Address
+
+ if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex))))
+ {
+ *address = inet_addr( pAdapter->IpAddressList.IpAddress.String );
+ *ifIndex = pAdapter->Index;
+ err = kNoErr;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+ // If we found the right interface, or we weren't trying to find a specific interface then we're done
+
+ if ( !err || !( *ifIndex) )
+ {
+ done = mDNStrue;
+ }
+
+ // Otherwise, try again by wildcarding the interface
+
+ else
+ {
+ *ifIndex = 0;
+ }
+ }
+
+exit:
+
+ if ( pAdapterInfo != NULL )
+ {
+ free( pAdapterInfo );
+ }
+
+ return( err );
+}
+
+
+static bool
+IsNortelVPN( IP_ADAPTER_INFO * pAdapter )
+{
+ return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
+ (pAdapter->AddressLength == 6) &&
+ (pAdapter->Address[0] == 0x44) &&
+ (pAdapter->Address[1] == 0x45) &&
+ (pAdapter->Address[2] == 0x53) &&
+ (pAdapter->Address[3] == 0x54) &&
+ (pAdapter->Address[4] == 0x42) &&
+ (pAdapter->Address[5] == 0x00)) ? true : false;
+}
+
+
+static bool
+IsJuniperVPN( IP_ADAPTER_INFO * pAdapter )
+{
+ return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description ) ) != NULL ) ? true : false;
+}
+
+
+static bool
+IsCiscoVPN( IP_ADAPTER_INFO * pAdapter )
+{
+ return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
+ (pAdapter->AddressLength == 6) &&
+ (pAdapter->Address[0] == 0x00) &&
+ (pAdapter->Address[1] == 0x05) &&
+ (pAdapter->Address[2] == 0x9a) &&
+ (pAdapter->Address[3] == 0x3c) &&
+ (pAdapter->Address[4] == 0x7a) &&
+ (pAdapter->Address[5] == 0x00)) ? true : false;
+}
+
+
+static const char *
+strnistr( const char * string, const char * subString, size_t max )
+{
+ size_t subStringLen;
+ size_t offset;
+ size_t maxOffset;
+ size_t stringLen;
+ const char * pPos;
+
+ if ( ( string == NULL ) || ( subString == NULL ) )
+ {
+ return string;
+ }
+
+ stringLen = ( max > strlen( string ) ) ? strlen( string ) : max;
+
+ if ( stringLen == 0 )
+ {
+ return NULL;
+ }
+
+ subStringLen = strlen( subString );
+
+ if ( subStringLen == 0 )
+ {
+ return string;
+ }
+
+ if ( subStringLen > stringLen )
+ {
+ return NULL;
+ }
+
+ maxOffset = stringLen - subStringLen;
+ pPos = string;
+
+ for ( offset = 0; offset <= maxOffset; offset++ )
+ {
+ if ( _strnicmp( pPos, subString, subStringLen ) == 0 )
+ {
+ return pPos;
+ }
+
+ pPos++;
+ }
+
+ return NULL;
+}
+
diff --git a/src/tools/mdnssd/Service.h b/src/tools/mdnssd/Service.h
new file mode 100644
index 0000000000..0b806f002b
--- /dev/null
+++ b/src/tools/mdnssd/Service.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MDNS_SERVICE_H__
+#define __MDNS_SERVICE_H__
+
+
+#include <windows.h>
+
+
+extern int RunDirect( int argc, LPTSTR argv[] );
+extern int Main( int argc, LPTSTR argv[] );
+
+
+#endif
+
diff --git a/src/tools/mdnssd/Service.rc b/src/tools/mdnssd/Service.rc
new file mode 100644
index 0000000000..92c2883dc9
--- /dev/null
+++ b/src/tools/mdnssd/Service.rc
@@ -0,0 +1,115 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+//#include "winres.h"
+//#include "WinVersRes.h"
+#include "windows.h"
+#include "EventLog.rc"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)\0"
+ VALUE "FileDescription", "Bonjour Service Fallback\0"
+ VALUE "FileVersion", 1,0,0,0
+ VALUE "InternalName", "mdnssd.exe\0"
+ VALUE "LegalCopyright", "Copyright(c)2003-2012, Apple Computer, 2012 Nokia Corporation and/or its subsidiary(-ies).\0"
+ VALUE "OriginalFilename", "mdnssd.exe\0"
+ VALUE "ProductName", "Bonjour Fallback\0"
+ VALUE "ProductVersion", "1.0\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "#include ""WinVersRes.h""\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_SERVICE_DESCRIPTION "Enables hardware devices and software services to automatically configure themselves on the network and advertise their presence, so that users can discover and use those services without any unnecessary manual setup or administration."
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/tools/mdnssd/mDNS.c b/src/tools/mdnssd/mDNS.c
index 9de7f92c18..d93f545152 100644
--- a/src/tools/mdnssd/mDNS.c
+++ b/src/tools/mdnssd/mDNS.c
@@ -1884,7 +1884,7 @@ mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum)
while (length > 0) { length -= 2; sum += *ptr++; }
sum = (sum & 0xFFFF) + (sum >> 16);
sum = (sum & 0xFFFF) + (sum >> 16);
- return(sum != 0xFFFF ? sum : 0);
+ return (mDNSu16)(sum != 0xFFFF ? sum : 0);
}
mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length)
@@ -1892,10 +1892,10 @@ mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *co
IPv6PseudoHeader ph;
ph.src = *src;
ph.dst = *dst;
- ph.len.b[0] = length >> 24;
- ph.len.b[1] = length >> 16;
- ph.len.b[2] = length >> 8;
- ph.len.b[3] = length;
+ ph.len.b[0] = (0xFF & (length >> 24));
+ ph.len.b[1] = (0xFF & (length >> 16));
+ ph.len.b[2] = (0xFF & (length >> 8));
+ ph.len.b[3] = (0xFF & length);
ph.pro.b[0] = 0;
ph.pro.b[1] = 0;
ph.pro.b[2] = 0;
@@ -7706,23 +7706,23 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
}
}
-mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout)
+mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout)
{
McastResolver **p = &m->McastResolvers;
McastResolver *tmp = mDNSNULL;
if (!d) d = (const domainname *)"";
- LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
+ LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface1, timeout);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
while (*p) // Check if we already have this {interface, domain} tuple registered
{
- if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d))
+ if ((*p)->interface1 == interface1 && SameDomainName(&(*p)->domain, d))
{
- if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface);
+ if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface1);
(*p)->flags &= ~DNSServer_FlagDelete;
tmp = *p;
*p = tmp->next;
@@ -7740,7 +7740,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname
if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc");
else
{
- (*p)->interface = interface;
+ (*p)->interface1 = interface1;
(*p)->flags = DNSServer_FlagNew;
(*p)->timeout = timeout;
AssignDomainName(&(*p)->domain, d);
@@ -7905,13 +7905,13 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
//
// Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout
- if (curr->scoped && curr->interface == mDNSInterface_Any)
+ if (curr->scoped && curr->interface1 == mDNSInterface_Any)
{ debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; }
currcount = CountLabels(&curr->domain);
if ((!DEQuery || !curr->cellIntf) &&
((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) ||
- (curr->interface == question->InterfaceID)))
+ (curr->interface1 == question->InterfaceID)))
{
bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
@@ -7926,7 +7926,7 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
" Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout,
- curr->interface);
+ curr->interface1);
timeout += curr->timeout;
if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf);
bit_set_opaque64(question->validDNSServers, index);
@@ -7993,7 +7993,7 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
// is the new way of specifying an InterfaceID option for DNSServer. These will be considered
// only when the question has non-zero interfaceID.
- if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID))
+ if ((!curr->scoped && !InterfaceID) || (curr->interface1 == InterfaceID))
{
// If we know that all the names are already equally good matches, then skip calling BetterMatchForName.
@@ -9065,7 +9065,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
query->ServiceInfoQueryContext = Context;
// info->name = Must already be set up by client
-// info->interface = Must already be set up by client
+// info->interface1 = Must already be set up by client
info->ip = zeroAddr;
info->port = zeroIPPort;
info->TXTlen = 0;
@@ -9713,9 +9713,9 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
// 3. Any DNS servers specific to this interface are now unusable
for (s = m->DNSServers; s; s = s->next)
- if (s->interface == set->InterfaceID)
+ if (s->interface1 == set->InterfaceID)
{
- s->interface = mDNSInterface_Any;
+ s->interface1 = mDNSInterface_Any;
s->teststate = DNSServer_Disabled;
}
}
diff --git a/src/tools/mdnssd/mDNSEmbeddedAPI.h b/src/tools/mdnssd/mDNSEmbeddedAPI.h
index 7310dc56e3..44bcaaa22f 100755
--- a/src/tools/mdnssd/mDNSEmbeddedAPI.h
+++ b/src/tools/mdnssd/mDNSEmbeddedAPI.h
@@ -1054,7 +1054,7 @@ enum
typedef struct McastResolver
{
struct McastResolver *next;
- mDNSInterfaceID interface;
+ mDNSInterfaceID interface1;
mDNSu32 flags; // Set when we're planning to delete this from the list
domainname domain;
mDNSu32 timeout; // timeout value for questions
@@ -1063,7 +1063,7 @@ typedef struct McastResolver
typedef struct DNSServer
{
struct DNSServer *next;
- mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces
+ mDNSInterfaceID interface1; // For specialized uses; we can have DNS servers reachable over specific interfaces
mDNSAddr addr;
mDNSIPPort port;
mDNSOpaque16 testid;
@@ -2469,11 +2469,11 @@ extern void RecreateNATMappings(mDNS *const m);
extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router);
-extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf);
+extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf);
extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID);
-extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout);
+extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout);
// We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
#define mDNS_AddSearchDomain_CString(X, I) \
diff --git a/src/tools/mdnssd/mDNSPosix.c b/src/tools/mdnssd/mDNSPosix.c
index a1868709db..091cbc6456 100755
--- a/src/tools/mdnssd/mDNSPosix.c
+++ b/src/tools/mdnssd/mDNSPosix.c
@@ -98,6 +98,8 @@ static int num_registered_interfaces = 0;
static int num_pkts_accepted = 0;
static int num_pkts_rejected = 0;
+// number of accumulated select errors (to decide if a reset is required).
+static int nSelectErrors = 0;
// ***************************************************************************
// Functions
@@ -1441,6 +1443,9 @@ mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct ti
if (timeout->tv_sec > interval.tv_sec ||
(timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec))
*timeout = interval;
+ // cope well with vey large changes in time (for example after sleep)
+ if (timeout->tv_sec > 1000) timeout->tv_sec = 1000;
+ if (timeout->tv_usec > 999999) timeout->tv_usec = 999999;
}
mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds)
@@ -1582,9 +1587,28 @@ mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout,
sigset_t *pSignalsReceived, mDNSBool *pDataDispatched)
{
fd_set listenFDs = gEventFDs;
- int fdMax = 0, numReady;
+ int fdMax = 0, numReady, hadError = 0;
struct timeval timeout = *pTimeout;
-
+
+ if (nSelectErrors > 20)
+ {
+ LogMsg("ERROR: accumulated too many consecutive select errors, trying to to a sleep/wake cycle");
+ mDNSCoreMachineSleep(m, mDNStrue);
+ mDNSCoreMachineSleep(m, mDNSfalse);
+ nSelectErrors = 0;
+ }
+ if (timeout.tv_sec < 0)
+ {
+ LogMsg("ERROR: negative timeout: tv_sec %ld %d\n", (long)timeout.tv_sec, (int)mDNSPlatformOneSecond);
+ timeout.tv_sec = 1;
+ hadError = ++nSelectErrors;
+ }
+ if (timeout.tv_usec < 0)
+ {
+ LogMsg("ERROR: negative timeout: tv_usec %ld %d\n", (long)timeout.tv_usec, (int)mDNSPlatformOneSecond);
+ timeout.tv_usec = 1000;
+ hadError = ++nSelectErrors;
+ }
// Include the sockets that are listening to the wire in our select() set
mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified
if (fdMax < gMaxFD)
@@ -1607,10 +1631,17 @@ mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout,
break; // in case callback removed elements from gEventSources
}
}
+ nSelectErrors = hadError;
*pDataDispatched = mDNStrue;
}
else
+ {
+ if (numReady < 0 && errno != EINTR)
+ ++nSelectErrors;
+ else if (numReady == 0)
+ nSelectErrors = hadError; // timout reached
*pDataDispatched = mDNSfalse;
+ }
(void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL);
*pSignalsReceived = gEventSignals;
diff --git a/src/tools/mdnssd/mDNSWin32.c b/src/tools/mdnssd/mDNSWin32.c
new file mode 100644
index 0000000000..c9a674f9bc
--- /dev/null
+++ b/src/tools/mdnssd/mDNSWin32.c
@@ -0,0 +1,5018 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To Do:
+
+ - Get unicode name of machine for nice name instead of just the host name.
+ - Use the IPv6 Internet Connection Firewall API to allow IPv6 mDNS without manually changing the firewall.
+ - Get DNS server address(es) from Windows and provide them to the uDNS layer.
+ - Implement TCP support for truncated packets (only stubs now).
+
+*/
+
+#define _CRT_RAND_S
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <crtdbg.h>
+#include <string.h>
+
+#include "Poll.h"
+#include "CommonServices.h"
+#include "DebugServices.h"
+#include "Firewall.h"
+#include "RegNames.h"
+#include "Secret.h"
+#include "dns_sd.h"
+
+#include <Iphlpapi.h>
+#include <mswsock.h>
+#include <process.h>
+#include <ntsecapi.h>
+#include <lm.h>
+#include <winioctl.h>
+#include <ntddndis.h> // This defines the IOCTL constants.
+
+#include "mDNSEmbeddedAPI.h"
+#include "GenLinkedList.h"
+#include "DNSCommon.h"
+#include "mDNSWin32.h"
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#define DEBUG_NAME "[mDNSWin32] "
+
+#define MDNS_WINDOWS_USE_IPV6_IF_ADDRS 1
+#define MDNS_WINDOWS_ENABLE_IPV4 1
+#define MDNS_WINDOWS_ENABLE_IPV6 1
+#define MDNS_FIX_IPHLPAPI_PREFIX_BUG 1
+#define MDNS_SET_HINFO_STRINGS 0
+
+#define kMDNSDefaultName "My Computer"
+
+#define kWinSockMajorMin 2
+#define kWinSockMinorMin 2
+
+#define kRegistryMaxKeyLength 255
+#define kRegistryMaxValueName 16383
+
+static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG;
+
+#define kIPv6IfIndexBase (10000000L)
+#define SMBPortAsNumber 445
+#define DEVICE_PREFIX "\\\\.\\"
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS );
+mDNSlocal mStatus SetupHostName( mDNS * const inMDNS );
+mDNSlocal mStatus SetupName( mDNS * const inMDNS );
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD );
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD );
+mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD );
+mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef );
+mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort );
+mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize );
+mDNSlocal int getifaddrs( struct ifaddrs **outAddrs );
+mDNSlocal void freeifaddrs( struct ifaddrs *inAddrs );
+
+
+
+// Platform Accessors
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
+struct mDNSPlatformInterfaceInfo
+{
+ const char * name;
+ mDNSAddr ip;
+};
+
+
+mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
+mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
+
+
+// Wakeup Structs
+
+#define kUnicastWakeupNumTries ( 1 )
+#define kUnicastWakeupSleepBetweenTries ( 0 )
+#define kMulticastWakeupNumTries ( 18 )
+#define kMulticastWakeupSleepBetweenTries ( 100 )
+
+typedef struct MulticastWakeupStruct
+{
+ mDNS *inMDNS;
+ struct sockaddr_in addr;
+ INT addrLen;
+ unsigned char data[ 102 ];
+ INT dataLen;
+ INT numTries;
+ INT msecSleep;
+} MulticastWakeupStruct;
+
+
+// Utilities
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs );
+#endif
+
+mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs );
+
+
+mDNSlocal DWORD GetPrimaryInterface();
+mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask );
+mDNSlocal mDNSBool CanReceiveUnicast( void );
+mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr );
+
+mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string );
+mDNSlocal mStatus RegQueryString( HKEY key, LPCSTR param, LPSTR * string, DWORD * stringLen, DWORD * enabled );
+mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh);
+mDNSlocal OSStatus TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize );
+mDNSlocal OSStatus WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize );
+mDNSlocal void CALLBACK TCPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
+mDNSlocal void TCPCloseSocket( TCPSocket * socket );
+mDNSlocal void CALLBACK UDPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
+mDNSlocal void UDPCloseSocket( UDPSocket * sock );
+mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa);
+mDNSlocal void GetDDNSFQDN( domainname *const fqdn );
+#ifdef UNICODE
+mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey );
+#else
+mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCSTR lpSubKey );
+#endif
+mDNSlocal void SetDomainSecrets( mDNS * const inMDNS );
+mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain );
+mDNSlocal VOID CALLBACK CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue );
+mDNSlocal void CheckFileShares( mDNS * const inMDNS );
+mDNSlocal void SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
+mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName );
+mDNSlocal void SendWakeupPacket( mDNS * const inMDNS, LPSOCKADDR addr, INT addrlen, const char * buf, INT buflen, INT numTries, INT msecSleep );
+mDNSlocal void _cdecl SendMulticastWakeupPacket( void *arg );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
+mDNSs32 mDNSPlatformOneSecond = 0;
+mDNSlocal UDPSocket * gUDPSockets = NULL;
+mDNSlocal int gUDPNumSockets = 0;
+mDNSlocal BOOL gEnableIPv6 = TRUE;
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+
+ typedef DWORD
+ ( WINAPI * GetAdaptersAddressesFunctionPtr )(
+ ULONG inFamily,
+ DWORD inFlags,
+ PVOID inReserved,
+ PIP_ADAPTER_ADDRESSES inAdapter,
+ PULONG outBufferSize );
+
+ mDNSlocal HMODULE gIPHelperLibraryInstance = NULL;
+ mDNSlocal GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL;
+
+#endif
+
+
+#ifndef HCRYPTPROV
+ typedef ULONG_PTR HCRYPTPROV; // WinCrypt.h, line 249
+#endif
+
+
+#ifndef CRYPT_MACHINE_KEYSET
+# define CRYPT_MACHINE_KEYSET 0x00000020
+#endif
+
+#ifndef CRYPT_NEWKEYSET
+# define CRYPT_NEWKEYSET 0x00000008
+#endif
+
+#ifndef PROV_RSA_FULL
+# define PROV_RSA_FULL 1
+#endif
+
+typedef BOOL (__stdcall *fnCryptGenRandom)( HCRYPTPROV, DWORD, BYTE* );
+typedef BOOL (__stdcall *fnCryptAcquireContext)( HCRYPTPROV*, LPCTSTR, LPCTSTR, DWORD, DWORD);
+typedef BOOL (__stdcall *fnCryptReleaseContext)(HCRYPTPROV, DWORD);
+
+static fnCryptAcquireContext g_lpCryptAcquireContext = NULL;
+static fnCryptReleaseContext g_lpCryptReleaseContext = NULL;
+static fnCryptGenRandom g_lpCryptGenRandom = NULL;
+static HINSTANCE g_hAAPI32 = NULL;
+static HCRYPTPROV g_hProvider = ( ULONG_PTR ) NULL;
+
+
+typedef DNSServiceErrorType ( DNSSD_API *DNSServiceRegisterFunc )
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name, /* may be NULL */
+ const char *regtype,
+ const char *domain, /* may be NULL */
+ const char *host, /* may be NULL */
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord, /* may be NULL */
+ DNSServiceRegisterReply callBack, /* may be NULL */
+ void *context /* may be NULL */
+ );
+
+
+typedef void ( DNSSD_API *DNSServiceRefDeallocateFunc )( DNSServiceRef sdRef );
+
+mDNSlocal HMODULE gDNSSDLibrary = NULL;
+mDNSlocal DNSServiceRegisterFunc gDNSServiceRegister = NULL;
+mDNSlocal DNSServiceRefDeallocateFunc gDNSServiceRefDeallocate = NULL;
+mDNSlocal HANDLE gSMBThread = NULL;
+mDNSlocal HANDLE gSMBThreadRegisterEvent = NULL;
+mDNSlocal HANDLE gSMBThreadDeregisterEvent = NULL;
+mDNSlocal HANDLE gSMBThreadStopEvent = NULL;
+mDNSlocal HANDLE gSMBThreadQuitEvent = NULL;
+
+#define kSMBStopEvent ( WAIT_OBJECT_0 + 0 )
+#define kSMBRegisterEvent ( WAIT_OBJECT_0 + 1 )
+#define kSMBDeregisterEvent ( WAIT_OBJECT_0 + 2 )
+
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Support ==
+#endif
+
+//===========================================================================================================================
+// mDNSPlatformInit
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformInit( mDNS * const inMDNS )
+{
+ mStatus err;
+ OSVERSIONINFO osInfo;
+ BOOL ok;
+ WSADATA wsaData;
+ int supported;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ int sa4len;
+ int sa6len;
+ DWORD size;
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "platform init\n" );
+
+ // Initialize variables. If the PlatformSupport pointer is not null then just assume that a non-Apple client is
+ // calling mDNS_Init and wants to provide its own storage for the platform-specific data so do not overwrite it.
+
+ mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) );
+ if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport;
+ inMDNS->p->mainThread = OpenThread( THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId() );
+ require_action( inMDNS->p->mainThread, exit, err = mStatus_UnknownErr );
+ inMDNS->p->checkFileSharesTimer = CreateWaitableTimer( NULL, FALSE, NULL );
+ require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr );
+ inMDNS->p->checkFileSharesTimeout = 10; // Retry time for CheckFileShares() in seconds
+ mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time
+
+ // Get OS version info
+
+ osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
+ ok = GetVersionEx( &osInfo );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+ inMDNS->p->osMajorVersion = osInfo.dwMajorVersion;
+ inMDNS->p->osMinorVersion = osInfo.dwMinorVersion;
+
+ // Don't enable IPv6 on anything less recent than Windows Vista
+
+ if ( inMDNS->p->osMajorVersion < 6 )
+ {
+ gEnableIPv6 = FALSE;
+ }
+
+ // Startup WinSock 2.2 or later.
+
+ err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData );
+ require_noerr( err, exit );
+
+ supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) );
+ require_action( supported, exit, err = mStatus_UnsupportedErr );
+
+ inMDNS->CanReceiveUnicastOn5353 = CanReceiveUnicast();
+
+ // Setup the HINFO HW strings.
+ //<rdar://problem/7245119> device-info should have model=Windows
+
+ strcpy_s( ( char* ) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2, "Windows" );
+ inMDNS->HIHardware.c[ 0 ] = ( mDNSu8 ) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] );
+ dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c );
+
+ // Setup the HINFO SW strings.
+#if ( MDNS_SET_HINFO_STRINGS )
+ mDNS_snprintf( (char *) &inMDNS->HISoftware.c[ 1 ], sizeof( inMDNS->HISoftware.c ) - 2,
+ "mDNSResponder (%s %s)", __DATE__, __TIME__ );
+ inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] );
+ dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c );
+#endif
+
+ // Set up the IPv4 unicast socket
+
+ inMDNS->p->unicastSock4.fd = INVALID_SOCKET;
+ inMDNS->p->unicastSock4.recvMsgPtr = NULL;
+ inMDNS->p->unicastSock4.ifd = NULL;
+ inMDNS->p->unicastSock4.next = NULL;
+ inMDNS->p->unicastSock4.m = inMDNS;
+
+#if ( MDNS_WINDOWS_ENABLE_IPV4 )
+
+ sa4.sin_family = AF_INET;
+ sa4.sin_addr.s_addr = INADDR_ANY;
+ err = SetupSocket( inMDNS, (const struct sockaddr*) &sa4, zeroIPPort, &inMDNS->p->unicastSock4.fd );
+ check_noerr( err );
+ sa4len = sizeof( sa4 );
+ err = getsockname( inMDNS->p->unicastSock4.fd, (struct sockaddr*) &sa4, &sa4len );
+ require_noerr( err, exit );
+ inMDNS->p->unicastSock4.port.NotAnInteger = sa4.sin_port;
+ inMDNS->UnicastPort4 = inMDNS->p->unicastSock4.port;
+ err = WSAIoctl( inMDNS->p->unicastSock4.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4.recvMsgPtr, sizeof( inMDNS->p->unicastSock4.recvMsgPtr ), &size, NULL, NULL );
+
+ if ( err )
+ {
+ inMDNS->p->unicastSock4.recvMsgPtr = NULL;
+ }
+
+ err = mDNSPollRegisterSocket( inMDNS->p->unicastSock4.fd, FD_READ, UDPSocketNotification, &inMDNS->p->unicastSock4 );
+ require_noerr( err, exit );
+
+#endif
+
+ // Set up the IPv6 unicast socket
+
+ inMDNS->p->unicastSock6.fd = INVALID_SOCKET;
+ inMDNS->p->unicastSock6.recvMsgPtr = NULL;
+ inMDNS->p->unicastSock6.ifd = NULL;
+ inMDNS->p->unicastSock6.next = NULL;
+ inMDNS->p->unicastSock6.m = inMDNS;
+
+#if ( MDNS_WINDOWS_ENABLE_IPV6 )
+
+ if ( gEnableIPv6 )
+ {
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_addr = in6addr_any;
+ sa6.sin6_scope_id = 0;
+
+ // This call will fail if the machine hasn't installed IPv6. In that case,
+ // the error will be WSAEAFNOSUPPORT.
+
+ err = SetupSocket( inMDNS, (const struct sockaddr*) &sa6, zeroIPPort, &inMDNS->p->unicastSock6.fd );
+ require_action( !err || ( err == WSAEAFNOSUPPORT ), exit, err = (mStatus) WSAGetLastError() );
+ err = kNoErr;
+
+ // If we weren't able to create the socket (because IPv6 hasn't been installed) don't do this
+
+ if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET )
+ {
+ sa6len = sizeof( sa6 );
+ err = getsockname( inMDNS->p->unicastSock6.fd, (struct sockaddr*) &sa6, &sa6len );
+ require_noerr( err, exit );
+ inMDNS->p->unicastSock6.port.NotAnInteger = sa6.sin6_port;
+ inMDNS->UnicastPort6 = inMDNS->p->unicastSock6.port;
+
+ err = WSAIoctl( inMDNS->p->unicastSock6.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock6.recvMsgPtr, sizeof( inMDNS->p->unicastSock6.recvMsgPtr ), &size, NULL, NULL );
+
+ if ( err != 0 )
+ {
+ inMDNS->p->unicastSock6.recvMsgPtr = NULL;
+ }
+
+ err = mDNSPollRegisterSocket( inMDNS->p->unicastSock6.fd, FD_READ, UDPSocketNotification, &inMDNS->p->unicastSock6 );
+ require_noerr( err, exit );
+ }
+ }
+
+#endif
+
+ // Notify core of domain secret keys
+
+ SetDomainSecrets( inMDNS );
+
+ // Success!
+
+ mDNSCoreInitComplete( inMDNS, err );
+
+
+exit:
+
+ if ( err )
+ {
+ mDNSPlatformClose( inMDNS );
+ }
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "platform init done (err=%d %m)\n", err, err );
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformClose
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformClose( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "platform close\n" );
+ check( inMDNS );
+
+ if ( gSMBThread != NULL )
+ {
+ dlog( kDebugLevelTrace, DEBUG_NAME "tearing down smb registration thread\n" );
+ SetEvent( gSMBThreadStopEvent );
+
+ if ( WaitForSingleObject( gSMBThreadQuitEvent, 5 * 1000 ) == WAIT_OBJECT_0 )
+ {
+ if ( gSMBThreadQuitEvent )
+ {
+ CloseHandle( gSMBThreadQuitEvent );
+ gSMBThreadQuitEvent = NULL;
+ }
+
+ if ( gSMBThreadStopEvent )
+ {
+ CloseHandle( gSMBThreadStopEvent );
+ gSMBThreadStopEvent = NULL;
+ }
+
+ if ( gSMBThreadDeregisterEvent )
+ {
+ CloseHandle( gSMBThreadDeregisterEvent );
+ gSMBThreadDeregisterEvent = NULL;
+ }
+
+ if ( gSMBThreadRegisterEvent )
+ {
+ CloseHandle( gSMBThreadRegisterEvent );
+ gSMBThreadRegisterEvent = NULL;
+ }
+
+ if ( gDNSSDLibrary )
+ {
+ FreeLibrary( gDNSSDLibrary );
+ gDNSSDLibrary = NULL;
+ }
+ }
+ else
+ {
+ LogMsg( "Unable to stop SMBThread" );
+ }
+
+ inMDNS->p->smbFileSharing = mDNSfalse;
+ inMDNS->p->smbPrintSharing = mDNSfalse;
+ }
+
+ // Tear everything down in reverse order to how it was set up.
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+ check( !inMDNS->p->inactiveInterfaceList );
+
+#if ( MDNS_WINDOWS_ENABLE_IPV4 )
+
+ UDPCloseSocket( &inMDNS->p->unicastSock4 );
+
+#endif
+
+#if ( MDNS_WINDOWS_ENABLE_IPV6 )
+
+ if ( gEnableIPv6 )
+ {
+ UDPCloseSocket( &inMDNS->p->unicastSock6 );
+ }
+
+#endif
+
+ // Free the DLL needed for IPv6 support.
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+ if( gIPHelperLibraryInstance )
+ {
+ gGetAdaptersAddressesFunctionPtr = NULL;
+
+ FreeLibrary( gIPHelperLibraryInstance );
+ gIPHelperLibraryInstance = NULL;
+ }
+#endif
+
+ if ( g_hAAPI32 )
+ {
+ // Release any resources
+
+ if ( g_hProvider && g_lpCryptReleaseContext )
+ {
+ ( g_lpCryptReleaseContext )( g_hProvider, 0 );
+ }
+
+ // Free the AdvApi32.dll
+
+ FreeLibrary( g_hAAPI32 );
+
+ // And reset all the data
+
+ g_lpCryptAcquireContext = NULL;
+ g_lpCryptReleaseContext = NULL;
+ g_lpCryptGenRandom = NULL;
+ g_hProvider = ( ULONG_PTR ) NULL;
+ g_hAAPI32 = NULL;
+ }
+
+ WSACleanup();
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" );
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformLock
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformLock( const mDNS * const inMDNS )
+{
+ ( void ) inMDNS;
+}
+
+//===========================================================================================================================
+// mDNSPlatformUnlock
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformUnlock( const mDNS * const inMDNS )
+{
+ ( void ) inMDNS;
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrCopy
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformStrCopy( void *inDst, const void *inSrc )
+{
+ check( inSrc );
+ check( inDst );
+
+ strcpy( (char *) inDst, (const char*) inSrc );
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrLen
+//===========================================================================================================================
+
+mDNSexport mDNSu32 mDNSPlatformStrLen( const void *inSrc )
+{
+ check( inSrc );
+
+ return( (mDNSu32) strlen( (const char *) inSrc ) );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemCopy
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ memcpy( inDst, inSrc, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemSame
+//===========================================================================================================================
+
+mDNSexport mDNSBool mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemZero
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
+{
+ check( inDst );
+
+ memset( inDst, 0, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemAllocate
+//===========================================================================================================================
+
+mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize )
+{
+ void * mem;
+
+ check( inSize > 0 );
+
+ mem = malloc( inSize );
+ check( mem );
+
+ return( mem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemFree
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformMemFree( void *inMem )
+{
+ check( inMem );
+
+ free( inMem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformRandomNumber
+//===========================================================================================================================
+
+mDNSexport mDNSu32 mDNSPlatformRandomNumber(void)
+{
+ unsigned int randomNumber;
+ errno_t err;
+
+ err = rand_s( &randomNumber );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( err )
+ {
+ randomNumber = rand();
+ }
+
+ return ( mDNSu32 ) randomNumber;
+}
+
+//===========================================================================================================================
+// mDNSPlatformTimeInit
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformTimeInit( void )
+{
+ // No special setup is required on Windows -- we just use GetTickCount().
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// mDNSPlatformRawTime
+//===========================================================================================================================
+
+mDNSexport mDNSs32 mDNSPlatformRawTime( void )
+{
+ return( (mDNSs32) GetTickCount() );
+}
+
+//===========================================================================================================================
+// mDNSPlatformUTC
+//===========================================================================================================================
+
+mDNSexport mDNSs32 mDNSPlatformUTC( void )
+{
+ return ( mDNSs32 ) time( NULL );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceNameToID
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inName );
+
+ // Search for an interface with the specified name,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( strcmp( ifd->name, inName ) == 0 )
+ {
+ break;
+ }
+ }
+ require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+
+ // Success!
+
+ if( outID )
+ {
+ *outID = (mDNSInterfaceID) ifd;
+ }
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceIDToInfo
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+
+ check( inMDNS );
+ check( inID );
+ check( outInfo );
+
+ // Search for an interface with the specified ID,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( ifd == (mDNSInterfaceData *) inID )
+ {
+ break;
+ }
+ }
+ require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+
+ // Success!
+
+ outInfo->name = ifd->name;
+ outInfo->ip = ifd->interfaceInfo.ip;
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceIDfromInterfaceIndex
+//===========================================================================================================================
+
+mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * const inMDNS, mDNSu32 inIndex )
+{
+ mDNSInterfaceID id;
+
+ id = mDNSNULL;
+ if( inIndex == kDNSServiceInterfaceIndexLocalOnly )
+ {
+ id = mDNSInterface_LocalOnly;
+ }
+ else if( inIndex != 0 )
+ {
+ mDNSInterfaceData * ifd;
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( ( ifd->scopeID == inIndex ) && ifd->interfaceInfo.InterfaceActive )
+ {
+ id = ifd->interfaceInfo.InterfaceID;
+ break;
+ }
+ }
+ check( ifd );
+ }
+ return( id );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceIndexfromInterfaceID
+//===========================================================================================================================
+
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange )
+{
+ mDNSu32 index;
+
+ (void) suppressNetworkChange;
+
+ index = 0;
+ if( inID == mDNSInterface_LocalOnly )
+ {
+ index = (mDNSu32) kDNSServiceInterfaceIndexLocalOnly;
+ }
+ else if( inID )
+ {
+ mDNSInterfaceData * ifd;
+
+ // Search active interfaces.
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( (mDNSInterfaceID) ifd == inID )
+ {
+ index = ifd->scopeID;
+ break;
+ }
+ }
+
+ // Search inactive interfaces too so remove events for inactive interfaces report the old interface index.
+
+ if( !ifd )
+ {
+ for( ifd = inMDNS->p->inactiveInterfaceList; ifd; ifd = ifd->next )
+ {
+ if( (mDNSInterfaceID) ifd == inID )
+ {
+ index = ifd->scopeID;
+ break;
+ }
+ }
+ }
+ check( ifd );
+ }
+ return( index );
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformTCPSocket
+//===========================================================================================================================
+
+TCPSocket *
+mDNSPlatformTCPSocket
+ (
+ mDNS * const m,
+ TCPSocketFlags flags,
+ mDNSIPPort * port
+ )
+{
+ TCPSocket * sock = NULL;
+ u_long on = 1; // "on" for setsockopt
+ struct sockaddr_in saddr;
+ int len;
+ mStatus err = mStatus_NoError;
+
+ DEBUG_UNUSED( m );
+
+ require_action( flags == 0, exit, err = mStatus_UnsupportedErr );
+
+ // Setup connection data object
+
+ sock = (TCPSocket *) malloc( sizeof( TCPSocket ) );
+ require_action( sock, exit, err = mStatus_NoMemoryErr );
+ mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
+ sock->fd = INVALID_SOCKET;
+ sock->flags = flags;
+ sock->m = m;
+
+ mDNSPlatformMemZero(&saddr, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl( INADDR_ANY );
+ saddr.sin_port = port->NotAnInteger;
+
+ // Create the socket
+
+ sock->fd = socket(AF_INET, SOCK_STREAM, 0);
+ err = translate_errno( sock->fd != INVALID_SOCKET, WSAGetLastError(), mStatus_UnknownErr );
+ require_noerr( err, exit );
+
+ // bind
+
+ err = bind( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) );
+ err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+ require_noerr( err, exit );
+
+ // Set it to be non-blocking
+
+ err = ioctlsocket( sock->fd, FIONBIO, &on );
+ err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+ require_noerr( err, exit );
+
+ // Get port number
+
+ mDNSPlatformMemZero( &saddr, sizeof( saddr ) );
+ len = sizeof( saddr );
+
+ err = getsockname( sock->fd, ( struct sockaddr* ) &saddr, &len );
+ err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+ require_noerr( err, exit );
+
+ port->NotAnInteger = saddr.sin_port;
+
+exit:
+
+ if ( err && sock )
+ {
+ TCPCloseSocket( sock );
+ free( sock );
+ sock = mDNSNULL;
+ }
+
+ return sock;
+}
+
+//===========================================================================================================================
+// mDNSPlatformTCPConnect
+//===========================================================================================================================
+
+mStatus
+mDNSPlatformTCPConnect
+ (
+ TCPSocket * sock,
+ const mDNSAddr * inDstIP,
+ mDNSOpaque16 inDstPort,
+ domainname * hostname,
+ mDNSInterfaceID inInterfaceID,
+ TCPConnectionCallback inCallback,
+ void * inContext
+ )
+{
+ struct sockaddr_in saddr;
+ mStatus err = mStatus_NoError;
+
+ DEBUG_UNUSED( hostname );
+ DEBUG_UNUSED( inInterfaceID );
+
+ if ( inDstIP->type != mDNSAddrType_IPv4 )
+ {
+ LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: operation not supported");
+ return mStatus_UnknownErr;
+ }
+
+ // Setup connection data object
+
+ sock->userCallback = inCallback;
+ sock->userContext = inContext;
+
+ mDNSPlatformMemZero(&saddr, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = inDstPort.NotAnInteger;
+ memcpy(&saddr.sin_addr, &inDstIP->ip.v4.NotAnInteger, sizeof(saddr.sin_addr));
+
+ // Try and do connect
+
+ err = connect( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) );
+ require_action( !err || ( WSAGetLastError() == WSAEWOULDBLOCK ), exit, err = mStatus_ConnFailed );
+ sock->connected = !err ? TRUE : FALSE;
+
+ err = mDNSPollRegisterSocket( sock->fd, FD_CONNECT | FD_READ | FD_CLOSE, TCPSocketNotification, sock );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( !err )
+ {
+ err = sock->connected ? mStatus_ConnEstablished : mStatus_ConnPending;
+ }
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformTCPAccept
+//===========================================================================================================================
+
+mDNSexport
+mDNSexport TCPSocket *mDNSPlatformTCPAccept( TCPSocketFlags flags, int fd )
+ {
+ TCPSocket * sock = NULL;
+ mStatus err = mStatus_NoError;
+
+ require_action( !flags, exit, err = mStatus_UnsupportedErr );
+
+ sock = malloc( sizeof( TCPSocket ) );
+ require_action( sock, exit, err = mStatus_NoMemoryErr );
+
+ mDNSPlatformMemZero( sock, sizeof( *sock ) );
+
+ sock->fd = fd;
+ sock->flags = flags;
+
+exit:
+
+ if ( err && sock )
+ {
+ free( sock );
+ sock = NULL;
+ }
+
+ return sock;
+ }
+
+
+//===========================================================================================================================
+// mDNSPlatformTCPCloseConnection
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock )
+{
+ check( sock );
+
+ if ( sock )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "mDNSPlatformTCPCloseConnection 0x%x:%d\n", sock, sock->fd );
+
+ if ( sock->fd != INVALID_SOCKET )
+ {
+ mDNSPollUnregisterSocket( sock->fd );
+ closesocket( sock->fd );
+ sock->fd = INVALID_SOCKET;
+ }
+
+ free( sock );
+ }
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformReadTCP
+//===========================================================================================================================
+
+mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned long inBufferSize, mDNSBool * closed )
+{
+ int nread;
+ OSStatus err;
+
+ *closed = mDNSfalse;
+ nread = recv( sock->fd, inBuffer, inBufferSize, 0 );
+ err = translate_errno( ( nread >= 0 ), WSAGetLastError(), mStatus_UnknownErr );
+
+ if ( nread > 0 )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "mDNSPlatformReadTCP: 0x%x:%d read %d bytes\n", sock, sock->fd, nread );
+ }
+ else if ( !nread )
+ {
+ *closed = mDNStrue;
+ }
+ else if ( err == WSAECONNRESET )
+ {
+ *closed = mDNStrue;
+ nread = 0;
+ }
+ else if ( err == WSAEWOULDBLOCK )
+ {
+ nread = 0;
+ }
+ else
+ {
+ LogMsg( "ERROR: mDNSPlatformReadTCP - recv: %d\n", err );
+ nread = -1;
+ }
+
+ return nread;
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformWriteTCP
+//===========================================================================================================================
+
+mDNSexport long mDNSPlatformWriteTCP( TCPSocket *sock, const char *inMsg, unsigned long inMsgSize )
+{
+ int nsent;
+ OSStatus err;
+
+ nsent = send( sock->fd, inMsg, inMsgSize, 0 );
+
+ err = translate_errno( ( nsent >= 0 ) || ( WSAGetLastError() == WSAEWOULDBLOCK ), WSAGetLastError(), mStatus_UnknownErr );
+ require_noerr( err, exit );
+
+ if ( nsent < 0)
+ {
+ nsent = 0;
+ }
+
+exit:
+
+ return nsent;
+}
+
+//===========================================================================================================================
+// mDNSPlatformTCPGetFD
+//===========================================================================================================================
+
+mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock )
+{
+ return ( int ) sock->fd;
+}
+
+
+
+//===========================================================================================================================
+// TCPSocketNotification
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+TCPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
+{
+ TCPSocket *tcpSock = ( TCPSocket* ) context;
+ TCPConnectionCallback callback;
+ int err;
+
+ DEBUG_UNUSED( sock );
+
+ require_action( tcpSock, exit, err = mStatus_BadParamErr );
+ callback = ( TCPConnectionCallback ) tcpSock->userCallback;
+ require_action( callback, exit, err = mStatus_BadParamErr );
+
+ if ( event && ( event->lNetworkEvents & FD_CONNECT ) )
+ {
+ if ( event->iErrorCode[ FD_CONNECT_BIT ] == 0 )
+ {
+ callback( tcpSock, tcpSock->userContext, mDNStrue, 0 );
+ tcpSock->connected = mDNStrue;
+ }
+ else
+ {
+ callback( tcpSock, tcpSock->userContext, mDNSfalse, event->iErrorCode[ FD_CONNECT_BIT ] );
+ }
+ }
+ else
+ {
+ callback( tcpSock, tcpSock->userContext, mDNSfalse, 0 );
+ }
+
+exit:
+
+ return;
+}
+
+
+
+//===========================================================================================================================
+// mDNSPlatformUDPSocket
+//===========================================================================================================================
+
+mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport)
+{
+ UDPSocket* sock = NULL;
+ mDNSIPPort port = requestedport;
+ mStatus err = mStatus_NoError;
+ unsigned i;
+
+ // Setup connection data object
+
+ sock = ( UDPSocket* ) malloc(sizeof( UDPSocket ) );
+ require_action( sock, exit, err = mStatus_NoMemoryErr );
+ memset( sock, 0, sizeof( UDPSocket ) );
+
+ // Create the socket
+
+ sock->fd = INVALID_SOCKET;
+ sock->recvMsgPtr = m->p->unicastSock4.recvMsgPtr;
+ sock->addr = m->p->unicastSock4.addr;
+ sock->ifd = NULL;
+ sock->m = m;
+
+ // Try at most 10000 times to get a unique random port
+
+ for (i=0; i<10000; i++)
+ {
+ struct sockaddr_in saddr;
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = 0;
+
+ // The kernel doesn't do cryptographically strong random port
+ // allocation, so we do it ourselves here
+
+ if (mDNSIPPortIsZero(requestedport))
+ {
+ port = mDNSOpaque16fromIntVal( ( mDNSu16 ) ( 0xC000 + mDNSRandom(0x3FFF) ) );
+ }
+
+ saddr.sin_port = port.NotAnInteger;
+
+ err = SetupSocket(m, ( struct sockaddr* ) &saddr, port, &sock->fd );
+ if (!err) break;
+ }
+
+ require_noerr( err, exit );
+
+ // Set the port
+
+ sock->port = port;
+
+ // Arm the completion routine
+
+ err = mDNSPollRegisterSocket( sock->fd, FD_READ, UDPSocketNotification, sock );
+ require_noerr( err, exit );
+
+ // Bookkeeping
+
+ sock->next = gUDPSockets;
+ gUDPSockets = sock;
+ gUDPNumSockets++;
+
+exit:
+
+ if ( err && sock )
+ {
+ UDPCloseSocket( sock );
+ free( sock );
+ sock = NULL;
+ }
+
+ return sock;
+}
+
+//===========================================================================================================================
+// mDNSPlatformUDPClose
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
+{
+ UDPSocket * current = gUDPSockets;
+ UDPSocket * last = NULL;
+
+ while ( current )
+ {
+ if ( current == sock )
+ {
+ if ( last == NULL )
+ {
+ gUDPSockets = sock->next;
+ }
+ else
+ {
+ last->next = sock->next;
+ }
+
+ UDPCloseSocket( sock );
+ free( sock );
+
+ gUDPNumSockets--;
+
+ break;
+ }
+
+ last = current;
+ current = current->next;
+ }
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformSendUDP
+//===========================================================================================================================
+
+mDNSexport mStatus
+ mDNSPlatformSendUDP(
+ const mDNS * const inMDNS,
+ const void * const inMsg,
+ const mDNSu8 * const inMsgEnd,
+ mDNSInterfaceID inInterfaceID,
+ UDPSocket * inSrcSocket,
+ const mDNSAddr * inDstIP,
+ mDNSIPPort inDstPort )
+{
+ SOCKET sendingsocket = INVALID_SOCKET;
+ mStatus err = mStatus_NoError;
+ mDNSInterfaceData * ifd = (mDNSInterfaceData*) inInterfaceID;
+ struct sockaddr_storage addr;
+ int n;
+
+ DEBUG_USE_ONLY( inMDNS );
+
+ n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) );
+ check( inMDNS );
+ check( inMsg );
+ check( inMsgEnd );
+ check( inDstIP );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "platform send %d bytes to %#a:%u\n", n, inDstIP, ntohs( inDstPort.NotAnInteger ) );
+
+ if( inDstIP->type == mDNSAddrType_IPv4 )
+ {
+ struct sockaddr_in * sa4;
+
+ sa4 = (struct sockaddr_in *) &addr;
+ sa4->sin_family = AF_INET;
+ sa4->sin_port = inDstPort.NotAnInteger;
+ sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger;
+ sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock4.fd;
+
+ if (inSrcSocket) { sendingsocket = inSrcSocket->fd; debugf("mDNSPlatformSendUDP using port %d, static port %d, sock %d", mDNSVal16(inSrcSocket->port), inMDNS->p->unicastSock4.fd, sendingsocket); }
+ }
+ else if( inDstIP->type == mDNSAddrType_IPv6 )
+ {
+ struct sockaddr_in6 * sa6;
+
+ sa6 = (struct sockaddr_in6 *) &addr;
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_port = inDstPort.NotAnInteger;
+ sa6->sin6_flowinfo = 0;
+ sa6->sin6_addr = *( (struct in6_addr *) &inDstIP->ip.v6 );
+ sa6->sin6_scope_id = 0; // Windows requires the scope ID to be zero. IPV6_MULTICAST_IF specifies interface.
+ sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock6.fd;
+ }
+ else
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "%s: dst is not an IPv4 or IPv6 address (type=%d)\n", __ROUTINE__, inDstIP->type );
+ err = mStatus_BadParamErr;
+ goto exit;
+ }
+
+ if (IsValidSocket(sendingsocket))
+ {
+ n = sendto( sendingsocket, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
+ err = translate_errno( n > 0, errno_compat(), kWriteErr );
+
+ if ( err )
+ {
+ // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations
+
+ if ( !mDNSAddressIsAllDNSLinkGroup( inDstIP ) && ( WSAGetLastError() == WSAEHOSTDOWN || WSAGetLastError() == WSAENETDOWN || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAENETUNREACH ) )
+ {
+ err = mStatus_TransientErr;
+ }
+ else
+ {
+ //require_noerr( err, exit );
+ }
+ }
+ }
+
+exit:
+ return( err );
+}
+
+
+mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
+ {
+ DEBUG_UNUSED( m );
+ DEBUG_UNUSED( InterfaceID );
+ }
+
+
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
+ {
+ DEBUG_UNUSED( m );
+ DEBUG_UNUSED( allowSleep );
+ DEBUG_UNUSED( reason );
+ }
+
+//===========================================================================================================================
+// mDNSPlatformSendRawPacket
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *ethaddr, char *ipaddr, int iteration)
+{
+ unsigned char mac[ 6 ];
+ unsigned char buf[ 102 ];
+ char hex[ 3 ] = { 0 };
+ unsigned char *bufPtr = buf;
+ struct sockaddr_storage saddr;
+ INT len = sizeof( saddr );
+ mDNSBool unicast = mDNSfalse;
+ MulticastWakeupStruct *info;
+ int i;
+ mStatus err;
+
+ (void) InterfaceID;
+
+ require_action( ethaddr, exit, err = mStatus_BadParamErr );
+
+ for ( i = 0; i < 6; i++ )
+ {
+ memcpy( hex, ethaddr + ( i * 3 ), 2 );
+ mac[ i ] = ( unsigned char ) strtoul( hex, NULL, 16 );
+ }
+
+ memset( buf, 0, sizeof( buf ) );
+
+ for ( i = 0; i < 6; i++ )
+ {
+ *bufPtr++ = 0xff;
+ }
+
+ for ( i = 0; i < 16; i++ )
+ {
+ memcpy( bufPtr, mac, sizeof( mac ) );
+ bufPtr += sizeof( mac );
+ }
+
+ if ( ipaddr )
+ {
+ if ( WSAStringToAddressA( ipaddr, AF_INET, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 )
+ {
+ struct sockaddr_in * saddr4 = ( struct sockaddr_in* ) &saddr;
+ saddr4->sin_port = htons( 9 );
+ len = sizeof( *saddr4 );
+
+ if ( saddr4->sin_addr.s_addr != htonl( INADDR_ANY ) )
+ {
+ unicast = mDNStrue;
+ }
+ }
+ else if ( WSAStringToAddressA( ipaddr, AF_INET6, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 )
+ {
+ mDNSInterfaceData *ifd = ( mDNSInterfaceData* ) InterfaceID;
+ struct sockaddr_in6 * saddr6 = ( struct sockaddr_in6* ) &saddr;
+ saddr6->sin6_port = htons( 9 );
+
+ if ( ifd != NULL )
+ {
+ saddr6->sin6_scope_id = ifd->scopeID;
+ }
+
+ len = sizeof( *saddr6 );
+
+ if ( memcmp( &saddr6->sin6_addr, &in6addr_any, sizeof( IN6_ADDR ) ) != 0 )
+ {
+ unicast = mDNStrue;
+ }
+ }
+ }
+
+ if ( ( iteration < 2 ) && ( unicast ) )
+ {
+ SendWakeupPacket( m, ( LPSOCKADDR ) &saddr, len, ( const char* ) buf, sizeof( buf ), kUnicastWakeupNumTries, kUnicastWakeupSleepBetweenTries );
+ }
+
+ info = ( MulticastWakeupStruct* ) malloc( sizeof( MulticastWakeupStruct ) );
+ require_action( info, exit, err = mStatus_NoMemoryErr );
+ info->inMDNS = m;
+ memset( &info->addr, 0, sizeof( info->addr ) );
+ info->addr.sin_family = AF_INET;
+ info->addr.sin_addr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
+ info->addr.sin_port = htons( 9 );
+ info->addrLen = sizeof( info->addr );
+ memcpy( info->data, buf, sizeof( buf ) );
+ info->dataLen = sizeof( buf );
+ info->numTries = kMulticastWakeupNumTries;
+ info->msecSleep = kMulticastWakeupSleepBetweenTries;
+
+ _beginthread( SendMulticastWakeupPacket, 0, ( void* ) info );
+
+exit:
+
+ return;
+}
+
+
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+{
+ DEBUG_UNUSED( rr );
+ DEBUG_UNUSED( intf );
+
+ return mDNStrue;
+}
+
+
+mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+ {
+ DEBUG_UNUSED( msg );
+ DEBUG_UNUSED( end );
+ DEBUG_UNUSED( InterfaceID );
+ }
+
+
+mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
+ {
+ DEBUG_UNUSED( m );
+ DEBUG_UNUSED( tpa );
+ DEBUG_UNUSED( tha );
+ DEBUG_UNUSED( InterfaceID );
+ }
+
+
+mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+ {
+ DEBUG_UNUSED( msg );
+ DEBUG_UNUSED( end );
+ DEBUG_UNUSED( InterfaceID );
+ }
+
+mDNSexport void mDNSPlatformSetLocalARP( const mDNSv4Addr * const tpa, const mDNSEthAddr * const tha, mDNSInterfaceID InterfaceID )
+ {
+ DEBUG_UNUSED( tpa );
+ DEBUG_UNUSED( tha );
+ DEBUG_UNUSED( InterfaceID );
+ }
+
+mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
+ {
+ dlog( kDebugLevelInfo, "%s\n", msg );
+ }
+
+mDNSexport void mDNSPlatformWriteLogMsg( const char * ident, const char * msg, mDNSLogLevel_t loglevel )
+ {
+ extern mDNS mDNSStorage;
+ int type;
+
+ DEBUG_UNUSED( ident );
+
+ type = EVENTLOG_ERROR_TYPE;
+
+ switch (loglevel)
+ {
+ case MDNS_LOG_MSG: type = EVENTLOG_ERROR_TYPE; break;
+ case MDNS_LOG_OPERATION: type = EVENTLOG_WARNING_TYPE; break;
+ case MDNS_LOG_SPS: type = EVENTLOG_INFORMATION_TYPE; break;
+ case MDNS_LOG_INFO: type = EVENTLOG_INFORMATION_TYPE; break;
+ case MDNS_LOG_DEBUG: type = EVENTLOG_INFORMATION_TYPE; break;
+ default:
+ fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel);
+ fflush(stderr);
+ }
+
+ mDNSStorage.p->reportStatusFunc( type, msg );
+ dlog( kDebugLevelInfo, "%s\n", msg );
+ }
+
+mDNSexport void mDNSPlatformSourceAddrForDest( mDNSAddr * const src, const mDNSAddr * const dst )
+ {
+ DEBUG_UNUSED( src );
+ DEBUG_UNUSED( dst );
+ }
+
+//===========================================================================================================================
+// mDNSPlatformTLSSetupCerts
+//===========================================================================================================================
+
+mDNSexport mStatus
+mDNSPlatformTLSSetupCerts(void)
+{
+ return mStatus_UnsupportedErr;
+}
+
+//===========================================================================================================================
+// mDNSPlatformTLSTearDownCerts
+//===========================================================================================================================
+
+mDNSexport void
+mDNSPlatformTLSTearDownCerts(void)
+{
+}
+
+//===========================================================================================================================
+// mDNSPlatformSetDNSConfig
+//===========================================================================================================================
+
+mDNSlocal void SetDNSServers( mDNS *const m );
+mDNSlocal void SetSearchDomainList( void );
+
+mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains)
+{
+ if (setservers) SetDNSServers(m);
+ if (setsearch) SetSearchDomainList();
+
+ if ( fqdn )
+ {
+ GetDDNSFQDN( fqdn );
+ }
+
+ if ( browseDomains )
+ {
+ GetDDNSDomains( browseDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains );
+ }
+
+ if ( regDomains )
+ {
+ GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
+ }
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformDynDNSHostNameStatusChanged
+//===========================================================================================================================
+
+mDNSexport void
+mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
+{
+ char uname[MAX_ESCAPED_DOMAIN_NAME];
+ BYTE bStatus;
+ LPCTSTR name;
+ HKEY key = NULL;
+ mStatus err;
+ char * p;
+
+ ConvertDomainNameToCString(dname, uname);
+
+ p = uname;
+
+ while (*p)
+ {
+ *p = (char) tolower(*p);
+ if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot
+ p++;
+ }
+
+ check( strlen( p ) <= MAX_ESCAPED_DOMAIN_NAME );
+ name = kServiceParametersNode TEXT("\\DynDNS\\State\\HostNames");
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, name, &key );
+ require_noerr( err, exit );
+
+ bStatus = ( status ) ? 0 : 1;
+ err = RegSetValueEx( key, kServiceDynDNSStatus, 0, REG_DWORD, (const LPBYTE) &bStatus, sizeof(DWORD) );
+ require_noerr( err, exit );
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ return;
+}
+
+
+//===========================================================================================================================
+// SetDomainSecrets
+//===========================================================================================================================
+
+// This routine needs to be called whenever the system secrets database changes.
+// We call it from DynDNSConfigDidChange and mDNSPlatformInit
+
+void
+SetDomainSecrets( mDNS * const m )
+{
+ DomainAuthInfo *ptr;
+ domainname fqdn;
+ DNameListElem * regDomains = NULL;
+
+ // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds.
+ // In the case where the user simultaneously removes their DDNS host name and the key
+ // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the
+ // server before it loses access to the necessary key. Otherwise, we'd leave orphaned
+ // address records behind that we no longer have permission to delete.
+
+ for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
+ ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10);
+
+ GetDDNSFQDN( &fqdn );
+
+ if ( fqdn.c[ 0 ] )
+ {
+ SetDomainSecret( m, &fqdn );
+ }
+
+ GetDDNSDomains( &regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
+
+ while ( regDomains )
+ {
+ DNameListElem * current = regDomains;
+ SetDomainSecret( m, &current->name );
+ regDomains = regDomains->next;
+ free( current );
+ }
+}
+
+
+//===========================================================================================================================
+// SetSearchDomainList
+//===========================================================================================================================
+
+mDNSlocal void SetDomainFromDHCP( void );
+mDNSlocal void SetReverseMapSearchDomainList( void );
+
+mDNSlocal void
+SetSearchDomainList( void )
+{
+ char * searchList = NULL;
+ DWORD searchListLen;
+ //DNameListElem * head = NULL;
+ //DNameListElem * current = NULL;
+ char * tok;
+ HKEY key;
+ mStatus err;
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &key );
+ require_noerr( err, exit );
+
+ err = RegQueryString( key, "SearchList", &searchList, &searchListLen, NULL );
+ require_noerr( err, exit );
+
+ // Windows separates the search domains with ','
+
+ tok = strtok( searchList, "," );
+ while ( tok )
+ {
+ if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) )
+ mDNS_AddSearchDomain_CString(tok, mDNSNULL);
+ tok = strtok( NULL, "," );
+ }
+
+exit:
+
+ if ( searchList )
+ {
+ free( searchList );
+ }
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+
+ SetDomainFromDHCP();
+ SetReverseMapSearchDomainList();
+}
+
+
+//===========================================================================================================================
+// SetReverseMapSearchDomainList
+//===========================================================================================================================
+
+mDNSlocal void
+SetReverseMapSearchDomainList( void )
+{
+ struct ifaddrs * ifa;
+
+ ifa = myGetIfAddrs( 1 );
+ while (ifa)
+ {
+ mDNSAddr addr;
+
+ if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask)
+ {
+ mDNSAddr netmask;
+ char buffer[256];
+
+ if (!SetupAddr(&netmask, ifa->ifa_netmask))
+ {
+ sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3],
+ addr.ip.v4.b[2] & netmask.ip.v4.b[2],
+ addr.ip.v4.b[1] & netmask.ip.v4.b[1],
+ addr.ip.v4.b[0] & netmask.ip.v4.b[0]);
+ mDNS_AddSearchDomain_CString(buffer, mDNSNULL);
+ }
+ }
+
+ ifa = ifa->ifa_next;
+ }
+
+ return;
+}
+
+
+//===========================================================================================================================
+// SetDNSServers
+//===========================================================================================================================
+
+mDNSlocal void
+SetDNSServers( mDNS *const m )
+{
+ PIP_PER_ADAPTER_INFO pAdapterInfo = NULL;
+ FIXED_INFO * fixedInfo = NULL;
+ ULONG bufLen = 0;
+ IP_ADDR_STRING * dnsServerList;
+ IP_ADDR_STRING * ipAddr;
+ DWORD index;
+ int i = 0;
+ mStatus err = kUnknownErr;
+
+ // Get the primary interface.
+
+ index = GetPrimaryInterface();
+
+ // This should have the interface index of the primary index. Fall back in cases where
+ // it can't be determined.
+
+ if ( index )
+ {
+ bufLen = 0;
+
+ for ( i = 0; i < 100; i++ )
+ {
+ err = GetPerAdapterInfo( index, pAdapterInfo, &bufLen );
+
+ if ( err != ERROR_BUFFER_OVERFLOW )
+ {
+ break;
+ }
+
+ pAdapterInfo = (PIP_PER_ADAPTER_INFO) realloc( pAdapterInfo, bufLen );
+ require_action( pAdapterInfo, exit, err = mStatus_NoMemoryErr );
+ }
+
+ require_noerr( err, exit );
+
+ dnsServerList = &pAdapterInfo->DnsServerList;
+ }
+ else
+ {
+ bufLen = sizeof( FIXED_INFO );
+
+ for ( i = 0; i < 100; i++ )
+ {
+ if ( fixedInfo )
+ {
+ GlobalFree( fixedInfo );
+ fixedInfo = NULL;
+ }
+
+ fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen );
+ require_action( fixedInfo, exit, err = mStatus_NoMemoryErr );
+
+ err = GetNetworkParams( fixedInfo, &bufLen );
+
+ if ( err != ERROR_BUFFER_OVERFLOW )
+ {
+ break;
+ }
+ }
+
+ require_noerr( err, exit );
+
+ dnsServerList = &fixedInfo->DnsServerList;
+ }
+
+ for ( ipAddr = dnsServerList; ipAddr; ipAddr = ipAddr->Next )
+ {
+ mDNSAddr addr;
+ err = StringToAddress( &addr, ipAddr->IpAddress.String );
+ if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, DEFAULT_UDNS_TIMEOUT, mDNSfalse);
+ }
+
+exit:
+
+ if ( pAdapterInfo )
+ {
+ free( pAdapterInfo );
+ }
+
+ if ( fixedInfo )
+ {
+ GlobalFree( fixedInfo );
+ }
+}
+
+
+//===========================================================================================================================
+// SetDomainFromDHCP
+//===========================================================================================================================
+
+mDNSlocal void
+SetDomainFromDHCP( void )
+{
+ int i = 0;
+ IP_ADAPTER_INFO * pAdapterInfo;
+ IP_ADAPTER_INFO * pAdapter;
+ DWORD bufLen;
+ DWORD index;
+ HKEY key = NULL;
+ LPSTR domain = NULL;
+ DWORD dwSize;
+ mStatus err = mStatus_NoError;
+
+ pAdapterInfo = NULL;
+
+ for ( i = 0; i < 100; i++ )
+ {
+ err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+
+ if ( err != ERROR_BUFFER_OVERFLOW )
+ {
+ break;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
+ require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+ }
+
+ require_noerr( err, exit );
+
+ index = GetPrimaryInterface();
+
+ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
+ {
+ if ( pAdapter->IpAddressList.IpAddress.String &&
+ pAdapter->IpAddressList.IpAddress.String[0] &&
+ pAdapter->GatewayList.IpAddress.String &&
+ pAdapter->GatewayList.IpAddress.String[0] &&
+ ( !index || ( pAdapter->Index == index ) ) )
+ {
+ // Found one that will work
+
+ char keyName[1024];
+
+ _snprintf( keyName, 1024, "%s%s", "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\", pAdapter->AdapterName );
+
+ err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyName, &key );
+ require_noerr( err, exit );
+
+ err = RegQueryString( key, "Domain", &domain, &dwSize, NULL );
+ check_noerr( err );
+
+ if ( !domain || !domain[0] )
+ {
+ if ( domain )
+ {
+ free( domain );
+ domain = NULL;
+ }
+
+ err = RegQueryString( key, "DhcpDomain", &domain, &dwSize, NULL );
+ check_noerr( err );
+ }
+
+ if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL);
+
+ break;
+ }
+ }
+
+exit:
+
+ if ( pAdapterInfo )
+ {
+ free( pAdapterInfo );
+ }
+
+ if ( domain )
+ {
+ free( domain );
+ }
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+}
+
+
+//===========================================================================================================================
+// mDNSPlatformGetPrimaryInterface
+//===========================================================================================================================
+
+mDNSexport mStatus
+mDNSPlatformGetPrimaryInterface( mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router )
+{
+ IP_ADAPTER_INFO * pAdapterInfo;
+ IP_ADAPTER_INFO * pAdapter;
+ DWORD bufLen;
+ int i;
+ BOOL found;
+ DWORD index;
+ mStatus err = mStatus_NoError;
+
+ DEBUG_UNUSED( m );
+
+ *v6 = zeroAddr;
+
+ pAdapterInfo = NULL;
+ bufLen = 0;
+ found = FALSE;
+
+ for ( i = 0; i < 100; i++ )
+ {
+ err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+
+ if ( err != ERROR_BUFFER_OVERFLOW )
+ {
+ break;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
+ require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+ }
+
+ require_noerr( err, exit );
+
+ index = GetPrimaryInterface();
+
+ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
+ {
+ if ( pAdapter->IpAddressList.IpAddress.String &&
+ pAdapter->IpAddressList.IpAddress.String[0] &&
+ pAdapter->GatewayList.IpAddress.String &&
+ pAdapter->GatewayList.IpAddress.String[0] &&
+ ( StringToAddress( v4, pAdapter->IpAddressList.IpAddress.String ) == mStatus_NoError ) &&
+ ( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) &&
+ ( !index || ( pAdapter->Index == index ) ) )
+ {
+ // Found one that will work
+
+ if ( pAdapter->AddressLength == sizeof( m->PrimaryMAC ) )
+ {
+ memcpy( &m->PrimaryMAC, pAdapter->Address, pAdapter->AddressLength );
+ }
+
+ found = TRUE;
+ break;
+ }
+ }
+
+exit:
+
+ if ( pAdapterInfo )
+ {
+ free( pAdapterInfo );
+ }
+
+ return err;
+}
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// debugf_
+//===========================================================================================================================
+#if( MDNS_DEBUGMSGS )
+mDNSexport void debugf_( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelInfo, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+// verbosedebugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS > 1 )
+mDNSexport void verbosedebugf_( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelVerbose, "%s\n", buffer );
+}
+#endif
+
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Internals ==
+#endif
+
+
+//===========================================================================================================================
+// SetupNiceName
+//===========================================================================================================================
+
+mStatus SetupNiceName( mDNS * const inMDNS )
+{
+ HKEY descKey = NULL;
+ char utf8[ 256 ];
+ LPCTSTR s;
+ LPWSTR joinName;
+ NETSETUP_JOIN_STATUS joinStatus;
+ mStatus err = 0;
+ DWORD namelen;
+ BOOL ok;
+
+ check( inMDNS );
+
+ // Set up the nice name.
+ utf8[0] = '\0';
+
+ // First try and open the registry key that contains the computer description value
+ s = TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters");
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s, 0, KEY_READ, &descKey);
+ check_translated_errno( err == 0, errno_compat(), kNameErr );
+
+ if ( !err )
+ {
+ TCHAR desc[256];
+ DWORD descSize = sizeof( desc );
+
+ // look for the computer description
+ err = RegQueryValueEx( descKey, TEXT("srvcomment"), 0, NULL, (LPBYTE) &desc, &descSize);
+
+ if ( !err )
+ {
+ err = TCHARtoUTF8( desc, utf8, sizeof( utf8 ) );
+ }
+
+ if ( err )
+ {
+ utf8[ 0 ] = '\0';
+ }
+ }
+
+ // if we can't find it in the registry, then use the hostname of the machine
+ if ( err || ( utf8[ 0 ] == '\0' ) )
+ {
+ TCHAR hostname[256];
+
+ namelen = sizeof( hostname ) / sizeof( TCHAR );
+
+ ok = GetComputerNameExW( ComputerNamePhysicalDnsHostname, hostname, &namelen );
+ err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
+ check_noerr( err );
+
+ if( !err )
+ {
+ err = TCHARtoUTF8( hostname, utf8, sizeof( utf8 ) );
+ }
+
+ if ( err )
+ {
+ utf8[ 0 ] = '\0';
+ }
+ }
+
+ // if we can't get the hostname
+ if ( err || ( utf8[ 0 ] == '\0' ) )
+ {
+ // Invalidate name so fall back to a default name.
+
+ strcpy( utf8, kMDNSDefaultName );
+ }
+
+ utf8[ sizeof( utf8 ) - 1 ] = '\0';
+ inMDNS->nicelabel.c[ 0 ] = (mDNSu8) (strlen( utf8 ) < MAX_DOMAIN_LABEL ? strlen( utf8 ) : MAX_DOMAIN_LABEL);
+ memcpy( &inMDNS->nicelabel.c[ 1 ], utf8, inMDNS->nicelabel.c[ 0 ] );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] );
+
+ if ( descKey )
+ {
+ RegCloseKey( descKey );
+ }
+
+ ZeroMemory( inMDNS->p->nbname, sizeof( inMDNS->p->nbname ) );
+ ZeroMemory( inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
+
+ namelen = sizeof( inMDNS->p->nbname );
+ ok = GetComputerNameExA( ComputerNamePhysicalNetBIOS, inMDNS->p->nbname, &namelen );
+ check( ok );
+ if ( ok ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios name \"%s\"\n", inMDNS->p->nbname );
+
+ err = NetGetJoinInformation( NULL, &joinName, &joinStatus );
+ check ( err == NERR_Success );
+ if ( err == NERR_Success )
+ {
+ if ( ( joinStatus == NetSetupWorkgroupName ) || ( joinStatus == NetSetupDomainName ) )
+ {
+ err = TCHARtoUTF8( joinName, inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
+ check( !err );
+ if ( !err ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios domain/workgroup \"%s\"\n", inMDNS->p->nbdomain );
+ }
+
+ NetApiBufferFree( joinName );
+ joinName = NULL;
+ }
+
+ err = 0;
+
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupHostName
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupHostName( mDNS * const inMDNS )
+{
+ mStatus err = 0;
+ char tempString[ 256 ];
+ DWORD tempStringLen;
+ domainlabel tempLabel;
+ BOOL ok;
+
+ check( inMDNS );
+
+ // Set up the nice name.
+ tempString[ 0 ] = '\0';
+
+ // use the hostname of the machine
+ tempStringLen = sizeof( tempString );
+ ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen );
+ err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
+ check_noerr( err );
+
+ // if we can't get the hostname
+ if( err || ( tempString[ 0 ] == '\0' ) )
+ {
+ // Invalidate name so fall back to a default name.
+
+ strcpy( tempString, kMDNSDefaultName );
+ }
+
+ tempString[ sizeof( tempString ) - 1 ] = '\0';
+ tempLabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL );
+ memcpy( &tempLabel.c[ 1 ], tempString, tempLabel.c[ 0 ] );
+
+ // Set up the host name.
+
+ ConvertUTF8PstringToRFC1034HostLabel( tempLabel.c, &inMDNS->hostlabel );
+ if( inMDNS->hostlabel.c[ 0 ] == 0 )
+ {
+ // Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default.
+
+ MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
+ }
+
+ check( inMDNS->hostlabel.c[ 0 ] != 0 );
+
+ mDNS_SetFQDN( inMDNS );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] );
+
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupName
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupName( mDNS * const inMDNS )
+{
+ mStatus err = 0;
+
+ check( inMDNS );
+
+ err = SetupNiceName( inMDNS );
+ check_noerr( err );
+
+ err = SetupHostName( inMDNS );
+ check_noerr( err );
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// SetupInterfaceList
+//===========================================================================================================================
+
+mStatus SetupInterfaceList( mDNS * const inMDNS )
+{
+ mStatus err;
+ mDNSInterfaceData ** next;
+ mDNSInterfaceData * ifd;
+ struct ifaddrs * addrs;
+ struct ifaddrs * p;
+ struct ifaddrs * loopbackv4;
+ struct ifaddrs * loopbackv6;
+ u_int flagMask;
+ u_int flagTest;
+ mDNSBool foundv4;
+ mDNSBool foundv6;
+ mDNSBool foundUnicastSock4DestAddr;
+ mDNSBool foundUnicastSock6DestAddr;
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+
+ inMDNS->p->registeredLoopback4 = mDNSfalse;
+ inMDNS->p->nextDHCPLeaseExpires = 0x7FFFFFFF;
+ addrs = NULL;
+ foundv4 = mDNSfalse;
+ foundv6 = mDNSfalse;
+ foundUnicastSock4DestAddr = mDNSfalse;
+ foundUnicastSock6DestAddr = mDNSfalse;
+
+ // Tear down any existing interfaces that may be set up.
+
+ TearDownInterfaceList( inMDNS );
+
+ // Set up the name of this machine.
+
+ err = SetupName( inMDNS );
+ check_noerr( err );
+
+ // Set up IPv4 interface(s). We have to set up IPv4 first so any IPv6 interface with an IPv4-routable address
+ // can refer to the IPv4 interface when it registers to allow DNS AAAA records over the IPv4 interface.
+
+ err = getifaddrs( &addrs );
+ require_noerr( err, exit );
+
+ loopbackv4 = NULL;
+ loopbackv6 = NULL;
+ next = &inMDNS->p->interfaceList;
+
+ flagMask = IFF_UP | IFF_MULTICAST;
+ flagTest = IFF_UP | IFF_MULTICAST;
+
+#if( MDNS_WINDOWS_ENABLE_IPV4 )
+ for( p = addrs; p; p = p->ifa_next )
+ {
+ if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+ {
+ continue;
+ }
+ if( p->ifa_flags & IFF_LOOPBACK )
+ {
+ if( !loopbackv4 )
+ {
+ loopbackv4 = p;
+ }
+ continue;
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
+ p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
+
+ err = SetupInterface( inMDNS, p, &ifd );
+ require_noerr( err, exit );
+
+ // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
+ // register him, but we also want to note that we haven't found a v4 interface
+ // so that we register loopback so same host operations work
+
+ if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
+ {
+ foundv4 = mDNStrue;
+ }
+
+ if ( p->ifa_dhcpEnabled && ( p->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) )
+ {
+ inMDNS->p->nextDHCPLeaseExpires = p->ifa_dhcpLeaseExpires;
+ }
+
+ // If we're on a platform that doesn't have WSARecvMsg(), there's no way
+ // of determing the destination address of a packet that is sent to us.
+ // For multicast packets, that's easy to determine. But for the unicast
+ // sockets, we'll fake it by taking the address of the first interface
+ // that is successfully setup.
+
+ if ( !foundUnicastSock4DestAddr )
+ {
+ inMDNS->p->unicastSock4.addr = ifd->interfaceInfo.ip;
+ foundUnicastSock4DestAddr = TRUE;
+ }
+
+ *next = ifd;
+ next = &ifd->next;
+ ++inMDNS->p->interfaceCount;
+ }
+#endif
+
+ // Set up IPv6 interface(s) after IPv4 is set up (see IPv4 notes above for reasoning).
+
+#if( MDNS_WINDOWS_ENABLE_IPV6 )
+
+ if ( gEnableIPv6 )
+ {
+ for( p = addrs; p; p = p->ifa_next )
+ {
+ if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET6 ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+ {
+ continue;
+ }
+ if( p->ifa_flags & IFF_LOOPBACK )
+ {
+ if( !loopbackv6 )
+ {
+ loopbackv6 = p;
+ }
+ continue;
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
+ p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
+
+ err = SetupInterface( inMDNS, p, &ifd );
+ require_noerr( err, exit );
+
+ // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
+ // register him, but we also want to note that we haven't found a v4 interface
+ // so that we register loopback so same host operations work
+
+ if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
+ {
+ foundv6 = mDNStrue;
+ }
+
+ // If we're on a platform that doesn't have WSARecvMsg(), there's no way
+ // of determing the destination address of a packet that is sent to us.
+ // For multicast packets, that's easy to determine. But for the unicast
+ // sockets, we'll fake it by taking the address of the first interface
+ // that is successfully setup.
+
+ if ( !foundUnicastSock6DestAddr )
+ {
+ inMDNS->p->unicastSock6.addr = ifd->interfaceInfo.ip;
+ foundUnicastSock6DestAddr = TRUE;
+ }
+
+ *next = ifd;
+ next = &ifd->next;
+ ++inMDNS->p->interfaceCount;
+ }
+ }
+
+#endif
+
+ // If there are no real interfaces, but there is a loopback interface, use that so same-machine operations work.
+
+#if( !MDNS_WINDOWS_ENABLE_IPV4 && !MDNS_WINDOWS_ENABLE_IPV6 )
+
+ flagMask |= IFF_LOOPBACK;
+ flagTest |= IFF_LOOPBACK;
+
+ for( p = addrs; p; p = p->ifa_next )
+ {
+ if( !p->ifa_addr || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+ {
+ continue;
+ }
+ if( ( p->ifa_addr->sa_family != AF_INET ) && ( p->ifa_addr->sa_family != AF_INET6 ) )
+ {
+ continue;
+ }
+
+ v4loopback = p;
+ break;
+ }
+
+#endif
+
+ if ( !foundv4 && loopbackv4 )
+ {
+ dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
+ loopbackv4->ifa_name ? loopbackv4->ifa_name : "<null>", loopbackv4->ifa_extra.index, loopbackv4->ifa_addr );
+
+ err = SetupInterface( inMDNS, loopbackv4, &ifd );
+ require_noerr( err, exit );
+
+ inMDNS->p->registeredLoopback4 = mDNStrue;
+
+#if( MDNS_WINDOWS_ENABLE_IPV4 )
+
+ // If we're on a platform that doesn't have WSARecvMsg(), there's no way
+ // of determing the destination address of a packet that is sent to us.
+ // For multicast packets, that's easy to determine. But for the unicast
+ // sockets, we'll fake it by taking the address of the first interface
+ // that is successfully setup.
+
+ if ( !foundUnicastSock4DestAddr )
+ {
+ inMDNS->p->unicastSock4.addr = ifd->sock.addr;
+ foundUnicastSock4DestAddr = TRUE;
+ }
+#endif
+
+ *next = ifd;
+ next = &ifd->next;
+ ++inMDNS->p->interfaceCount;
+ }
+
+ if ( !foundv6 && loopbackv6 )
+ {
+ dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
+ loopbackv6->ifa_name ? loopbackv6->ifa_name : "<null>", loopbackv6->ifa_extra.index, loopbackv6->ifa_addr );
+
+ err = SetupInterface( inMDNS, loopbackv6, &ifd );
+ require_noerr( err, exit );
+
+#if( MDNS_WINDOWS_ENABLE_IPV6 )
+
+ if ( gEnableIPv6 )
+ {
+ // If we're on a platform that doesn't have WSARecvMsg(), there's no way
+ // of determing the destination address of a packet that is sent to us.
+ // For multicast packets, that's easy to determine. But for the unicast
+ // sockets, we'll fake it by taking the address of the first interface
+ // that is successfully setup.
+
+ if ( !foundUnicastSock6DestAddr )
+ {
+ inMDNS->p->unicastSock6.addr = ifd->sock.addr;
+ foundUnicastSock6DestAddr = TRUE;
+ }
+ }
+
+#endif
+
+ *next = ifd;
+ next = &ifd->next;
+ ++inMDNS->p->interfaceCount;
+ }
+
+ CheckFileShares( inMDNS );
+
+exit:
+ if( err )
+ {
+ TearDownInterfaceList( inMDNS );
+ }
+ if( addrs )
+ {
+ freeifaddrs( addrs );
+ }
+ dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list done (err=%d %m)\n", err, err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterfaceList
+//===========================================================================================================================
+
+mStatus TearDownInterfaceList( mDNS * const inMDNS )
+{
+ mDNSInterfaceData ** p;
+ mDNSInterfaceData * ifd;
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+
+ // Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache.
+ // Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache
+ // so that remove events that occur after an interface goes away can still report the correct interface.
+
+ p = &inMDNS->p->inactiveInterfaceList;
+ while( *p )
+ {
+ ifd = *p;
+ if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) ifd ) > 0 )
+ {
+ p = &ifd->next;
+ continue;
+ }
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "freeing unreferenced, inactive interface %#p %#a\n", ifd, &ifd->interfaceInfo.ip );
+ *p = ifd->next;
+
+ QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) ifd );
+ }
+
+ // Tear down all the interfaces.
+
+ while( inMDNS->p->interfaceList )
+ {
+ ifd = inMDNS->p->interfaceList;
+ inMDNS->p->interfaceList = ifd->next;
+
+ TearDownInterface( inMDNS, ifd );
+ }
+ inMDNS->p->interfaceCount = 0;
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list done\n" );
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SetupInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD )
+{
+ mDNSInterfaceData * ifd;
+ mDNSInterfaceData * p;
+ mStatus err;
+
+ ifd = NULL;
+ dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inIFA );
+ check( inIFA->ifa_addr );
+ check( outIFD );
+
+ // Allocate memory for the interface and initialize it.
+
+ ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
+ require_action( ifd, exit, err = mStatus_NoMemoryErr );
+ ifd->sock.fd = kInvalidSocketRef;
+ ifd->sock.ifd = ifd;
+ ifd->sock.next = NULL;
+ ifd->sock.m = inMDNS;
+ ifd->index = inIFA->ifa_extra.index;
+ ifd->scopeID = inIFA->ifa_extra.index;
+ check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) );
+ strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 );
+ ifd->name[ sizeof( ifd->name ) - 1 ] = '\0';
+
+ strncpy(ifd->interfaceInfo.ifname, inIFA->ifa_name, sizeof(ifd->interfaceInfo.ifname));
+ ifd->interfaceInfo.ifname[sizeof(ifd->interfaceInfo.ifname)-1] = 0;
+
+ // We always send and receive using IPv4, but to reduce traffic, we send and receive using IPv6 only on interfaces
+ // that have no routable IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being
+ // on a large configured network, which means there's a good chance that most or all the other devices on that
+ // network should also have v4. By doing this we lose the ability to talk to true v6-only devices on that link,
+ // but we cut the packet rate in half. At this time, reducing the packet rate is more important than v6-only
+ // devices on a large configured network, so we are willing to make that sacrifice.
+
+ ifd->interfaceInfo.McastTxRx = ( ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTTOPOINT ) ) ? mDNStrue : mDNSfalse;
+ ifd->interfaceInfo.InterfaceID = NULL;
+
+ for( p = inMDNS->p->interfaceList; p; p = p->next )
+ {
+ if ( strcmp( p->name, ifd->name ) == 0 )
+ {
+ if (!ifd->interfaceInfo.InterfaceID)
+ {
+ ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) p;
+ }
+
+ if ( ( inIFA->ifa_addr->sa_family != AF_INET ) &&
+ ( p->interfaceInfo.ip.type == mDNSAddrType_IPv4 ) &&
+ ( p->interfaceInfo.ip.ip.v4.b[ 0 ] != 169 || p->interfaceInfo.ip.ip.v4.b[ 1 ] != 254 ) )
+ {
+ ifd->interfaceInfo.McastTxRx = mDNSfalse;
+ }
+
+ break;
+ }
+ }
+
+ if ( !ifd->interfaceInfo.InterfaceID )
+ {
+ ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) ifd;
+ }
+
+ // Set up a socket for this interface (if needed).
+
+ if( ifd->interfaceInfo.McastTxRx )
+ {
+ DWORD size;
+
+ err = SetupSocket( inMDNS, inIFA->ifa_addr, MulticastDNSPort, &ifd->sock.fd );
+ require_noerr( err, exit );
+ ifd->sock.addr = ( inIFA->ifa_addr->sa_family == AF_INET6 ) ? AllDNSLinkGroup_v6 : AllDNSLinkGroup_v4;
+ ifd->sock.port = MulticastDNSPort;
+
+ // Get a ptr to the WSARecvMsg function, if supported. Otherwise, we'll fallback to recvfrom.
+
+ err = WSAIoctl( ifd->sock.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &ifd->sock.recvMsgPtr, sizeof( ifd->sock.recvMsgPtr ), &size, NULL, NULL );
+
+ if ( err )
+ {
+ ifd->sock.recvMsgPtr = NULL;
+ }
+ }
+
+ if ( inIFA->ifa_dhcpEnabled && ( inIFA->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) )
+ {
+ inMDNS->p->nextDHCPLeaseExpires = inIFA->ifa_dhcpLeaseExpires;
+ }
+
+ ifd->interfaceInfo.NetWake = inIFA->ifa_womp;
+
+ // Register this interface with mDNS.
+
+ err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL );
+ require_noerr( err, exit );
+
+ err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &ifd->interfaceInfo.mask, NULL );
+ require_noerr( err, exit );
+
+ memcpy( ifd->interfaceInfo.MAC.b, inIFA->ifa_physaddr, sizeof( ifd->interfaceInfo.MAC.b ) );
+
+ ifd->interfaceInfo.Advertise = ( mDNSu8 ) inMDNS->AdvertiseLocalAddresses;
+
+ if ( ifd->sock.fd != kInvalidSocketRef )
+ {
+ err = mDNSPollRegisterSocket( ifd->sock.fd, FD_READ, UDPSocketNotification, &ifd->sock );
+ require_noerr( err, exit );
+ }
+
+ err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo, mDNSfalse );
+ require_noerr( err, exit );
+ ifd->hostRegistered = mDNStrue;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Registered interface %##a with mDNS\n", inIFA->ifa_addr );
+
+ // Success!
+
+ *outIFD = ifd;
+ ifd = NULL;
+
+exit:
+
+ if( ifd )
+ {
+ TearDownInterface( inMDNS, ifd );
+ }
+ dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface done (err=%d %m)\n", err, err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD )
+{
+ check( inMDNS );
+ check( inIFD );
+
+ // Deregister this interface with mDNS.
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering interface %#a with mDNS\n", &inIFD->interfaceInfo.ip );
+
+ if( inIFD->hostRegistered )
+ {
+ inIFD->hostRegistered = mDNSfalse;
+ mDNS_DeregisterInterface( inMDNS, &inIFD->interfaceInfo, mDNSfalse );
+ }
+
+ // Tear down the multicast socket.
+
+ UDPCloseSocket( &inIFD->sock );
+
+ // If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps
+ // the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it.
+
+ if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) inIFD ) > 0 )
+ {
+ inIFD->next = inMDNS->p->inactiveInterfaceList;
+ inMDNS->p->inactiveInterfaceList = inIFD;
+ dlog( kDebugLevelInfo, DEBUG_NAME "deferring free of interface %#p %#a\n", inIFD, &inIFD->interfaceInfo.ip );
+ }
+ else
+ {
+ dlog( kDebugLevelInfo, DEBUG_NAME "freeing interface %#p %#a immediately\n", inIFD, &inIFD->interfaceInfo.ip );
+ QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) inIFD );
+ }
+
+ return( mStatus_NoError );
+}
+
+mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD )
+{
+ free( inIFD );
+}
+
+//===========================================================================================================================
+// SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef )
+{
+ mStatus err;
+ SocketRef sock;
+ int option;
+ DWORD bytesReturned = 0;
+ BOOL behavior = FALSE;
+
+ DEBUG_UNUSED( inMDNS );
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "setting up socket %##a\n", inAddr );
+ check( inMDNS );
+ check( outSocketRef );
+
+ // Set up an IPv4 or IPv6 UDP socket.
+
+ sock = socket( inAddr->sa_family, SOCK_DGRAM, IPPROTO_UDP );
+ err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Turn on reuse address option so multiple servers can listen for Multicast DNS packets,
+ // if we're creating a multicast socket
+
+ if ( !mDNSIPPortIsZero( port ) )
+ {
+ option = 1;
+ err = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+ }
+
+ // <rdar://problem/7894393> Bonjour for Windows broken on Windows XP
+ //
+ // Not sure why, but the default behavior for sockets is to behave incorrectly
+ // when using them in Overlapped I/O mode on XP. According to MSDN:
+ //
+ // SIO_UDP_CONNRESET (opcode setting: I, T==3)
+ // Windows XP: Controls whether UDP PORT_UNREACHABLE messages are reported. Set to TRUE to enable reporting.
+ // Set to FALSE to disable reporting.
+ //
+ // Packet traces from misbehaving Bonjour installations showed that ICMP port unreachable
+ // messages were being sent to us after we sent out packets to a multicast address. This is clearly
+ // incorrect behavior, but should be harmless. However, after receiving a port unreachable error, WinSock
+ // will no longer receive any packets from that socket, which is not harmless. This behavior is only
+ // seen on XP.
+ //
+ // So we turn off port unreachable reporting to make sure our sockets that are reading
+ // multicast packets function correctly under all circumstances.
+
+ err = WSAIoctl( sock, SIO_UDP_CONNRESET, &behavior, sizeof(behavior), NULL, 0, &bytesReturned, NULL, NULL );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ if( inAddr->sa_family == AF_INET )
+ {
+ mDNSv4Addr ipv4;
+ struct sockaddr_in sa4;
+ struct ip_mreq mreqv4;
+
+ // Bind the socket to the desired port
+
+ ipv4.NotAnInteger = ( (const struct sockaddr_in *) inAddr )->sin_addr.s_addr;
+ mDNSPlatformMemZero( &sa4, sizeof( sa4 ) );
+ sa4.sin_family = AF_INET;
+ sa4.sin_port = port.NotAnInteger;
+ sa4.sin_addr.s_addr = ipv4.NotAnInteger;
+
+ err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) );
+ check_translated_errno( err == 0, errno_compat(), kUnknownErr );
+
+ // Turn on option to receive destination addresses and receiving interface.
+
+ option = 1;
+ err = setsockopt( sock, IPPROTO_IP, IP_PKTINFO, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ if ( !mDNSIPPortIsZero( port ) )
+ {
+ // Join the all-DNS multicast group so we receive Multicast DNS packets
+
+ mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
+ mreqv4.imr_interface.s_addr = ipv4.NotAnInteger;
+ err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqv4, sizeof( mreqv4 ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Specify the interface to send multicast packets on this socket.
+
+ sa4.sin_addr.s_addr = ipv4.NotAnInteger;
+ err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &sa4.sin_addr, sizeof( sa4.sin_addr ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Enable multicast loopback so we receive multicast packets we send (for same-machine operations).
+
+ option = 1;
+ err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+ }
+
+ // Send unicast packets with TTL 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Send multicast packets with TTL 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ }
+ else if( inAddr->sa_family == AF_INET6 )
+ {
+ struct sockaddr_in6 * sa6p;
+ struct sockaddr_in6 sa6;
+ struct ipv6_mreq mreqv6;
+
+ sa6p = (struct sockaddr_in6 *) inAddr;
+
+ // Bind the socket to the desired port
+
+ mDNSPlatformMemZero( &sa6, sizeof( sa6 ) );
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_port = port.NotAnInteger;
+ sa6.sin6_flowinfo = 0;
+ sa6.sin6_addr = sa6p->sin6_addr;
+ sa6.sin6_scope_id = sa6p->sin6_scope_id;
+
+ err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) );
+ check_translated_errno( err == 0, errno_compat(), kUnknownErr );
+
+ // Turn on option to receive destination addresses and receiving interface.
+
+ option = 1;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // We only want to receive IPv6 packets (not IPv4-mapped IPv6 addresses) because we have a separate socket
+ // for IPv4, but the IPv6 stack in Windows currently doesn't support IPv4-mapped IPv6 addresses and doesn't
+ // support the IPV6_V6ONLY socket option so the following code would typically not be executed (or needed).
+
+ #if( defined( IPV6_V6ONLY ) && ! defined( WIN_32 ) )
+ option = 1;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+ #endif
+
+ if ( !mDNSIPPortIsZero( port ) )
+ {
+ // Join the all-DNS multicast group so we receive Multicast DNS packets.
+
+ mreqv6.ipv6mr_multiaddr = *( (struct in6_addr *) &AllDNSLinkGroup_v6.ip.v6 );
+ mreqv6.ipv6mr_interface = sa6p->sin6_scope_id;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqv6, sizeof( mreqv6 ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Specify the interface to send multicast packets on this socket.
+
+ option = (int) sa6p->sin6_scope_id;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Enable multicast loopback so we receive multicast packets we send (for same-machine operations).
+
+ option = 1;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+ }
+
+ // Send unicast packets with TTL 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+ // Send multicast packets with TTL 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &option, sizeof( option ) );
+ check_translated_errno( err == 0, errno_compat(), kOptionErr );
+ }
+ else
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inAddr->sa_family );
+ err = kUnsupportedErr;
+ goto exit;
+ }
+
+ // Success!
+
+ *outSocketRef = sock;
+ sock = kInvalidSocketRef;
+ err = mStatus_NoError;
+
+exit:
+ if( IsValidSocket( sock ) )
+ {
+ close_compat( sock );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort )
+{
+ mStatus err;
+
+ check( inSA );
+ check( outIP );
+
+ if( inSA->sa_family == AF_INET )
+ {
+ struct sockaddr_in * sa4;
+
+ sa4 = (struct sockaddr_in *) inSA;
+ outIP->type = mDNSAddrType_IPv4;
+ outIP->ip.v4.NotAnInteger = sa4->sin_addr.s_addr;
+ if( outPort )
+ {
+ outPort->NotAnInteger = sa4->sin_port;
+ }
+ err = mStatus_NoError;
+ }
+ else if( inSA->sa_family == AF_INET6 )
+ {
+ struct sockaddr_in6 * sa6;
+
+ sa6 = (struct sockaddr_in6 *) inSA;
+ outIP->type = mDNSAddrType_IPv6;
+ outIP->ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr );
+ if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) )
+ {
+ outIP->ip.v6.w[ 1 ] = 0;
+ }
+ if( outPort )
+ {
+ outPort->NotAnInteger = sa6->sin6_port;
+ }
+ err = mStatus_NoError;
+ }
+ else
+ {
+ dlog( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family %d", __ROUTINE__, inSA->sa_family );
+ err = mStatus_BadParamErr;
+ }
+ return( err );
+}
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// UDPSocketNotification
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+UDPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
+{
+ UDPSocket *udpSock = ( UDPSocket* ) context;
+ WSAMSG wmsg;
+ WSABUF wbuf;
+ struct sockaddr_storage sockSrcAddr; // This is filled in by the WSARecv* function
+ INT sockSrcAddrLen; // See above
+ mDNSAddr srcAddr;
+ mDNSInterfaceID iid;
+ mDNSIPPort srcPort;
+ mDNSAddr dstAddr;
+ mDNSIPPort dstPort;
+ uint8_t controlBuffer[ 128 ];
+ mDNSu8 * end;
+ int num;
+ DWORD numTries;
+ mStatus err;
+
+ DEBUG_UNUSED( sock );
+ DEBUG_UNUSED( event );
+
+ require_action( udpSock != NULL, exit, err = mStatus_BadStateErr );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, udpSock->fd );
+
+ // Initialize the buffer structure
+
+ wbuf.buf = (char *) &udpSock->packet;
+ wbuf.len = (u_long) sizeof( udpSock->packet );
+ sockSrcAddrLen = sizeof( sockSrcAddr );
+
+ numTries = 0;
+
+ do
+ {
+ if ( udpSock->recvMsgPtr )
+ {
+ DWORD size;
+
+ wmsg.name = ( LPSOCKADDR ) &sockSrcAddr;
+ wmsg.namelen = sockSrcAddrLen;
+ wmsg.lpBuffers = &wbuf;
+ wmsg.dwBufferCount = 1;
+ wmsg.Control.buf = ( CHAR* ) controlBuffer;
+ wmsg.Control.len = sizeof( controlBuffer );
+ wmsg.dwFlags = 0;
+
+ err = udpSock->recvMsgPtr( udpSock->fd, &wmsg, &size, NULL, NULL );
+ err = translate_errno( ( err == 0 ), (OSStatus) WSAGetLastError(), kUnknownErr );
+ num = ( int ) size;
+
+ // <rdar://problem/7824093> iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate
+ //
+ // There seems to be a bug in some network device drivers that involves calling WSARecvMsg().
+ // Although all the parameters to WSARecvMsg() are correct, it returns a
+ // WSAEFAULT error code when there is no actual error. We have found experientially that falling
+ // back to using WSARecvFrom() when this happens will work correctly.
+
+ if ( err == WSAEFAULT ) udpSock->recvMsgPtr = NULL;
+ }
+ else
+ {
+ DWORD flags = 0;
+
+ num = WSARecvFrom( udpSock->fd, &wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sockSrcAddr, &sockSrcAddrLen, NULL, NULL );
+ err = translate_errno( ( num >= 0 ), ( OSStatus ) WSAGetLastError(), kUnknownErr );
+ }
+
+ // According to MSDN <http://msdn.microsoft.com/en-us/library/ms741687(VS.85).aspx>:
+ //
+ // "WSAECONNRESET: For a UDP datagram socket, this error would indicate that a previous
+ // send operation resulted in an ICMP "Port Unreachable" message."
+ //
+ // Because this is the case, we want to ignore this error and try again. Just in case
+ // this is some kind of pathological condition, we'll break out of the retry loop
+ // after 100 iterations
+
+ require_action( !err || ( err == WSAECONNRESET ) || ( err == WSAEFAULT ), exit, err = WSAGetLastError() );
+ }
+ while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) );
+
+ require_noerr( err, exit );
+
+ // Translate the source of this packet into mDNS data types
+
+ SockAddrToMDNSAddr( (struct sockaddr* ) &sockSrcAddr, &srcAddr, &srcPort );
+
+ // Initialize the destination of this packet. Just in case
+ // we can't determine this info because we couldn't call
+ // WSARecvMsg (recvMsgPtr)
+
+ dstAddr = udpSock->addr;
+ dstPort = udpSock->port;
+
+ if ( udpSock->recvMsgPtr )
+ {
+ LPWSACMSGHDR header;
+ LPWSACMSGHDR last = NULL;
+ int count = 0;
+
+ // Parse the control information. Reject packets received on the wrong interface.
+
+ // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
+ //
+ // There seems to be an interaction between Bullguard and this next bit of code.
+ // When a user's machine is running Bullguard, the control information that is
+ // returned is corrupted, and the code would go into an infinite loop. We'll add
+ // two bits of defensive coding here. The first will check that each pointer to
+ // the LPWSACMSGHDR that is returned in the for loop is different than the last.
+ // This fixes the problem with Bullguard. The second will break out of this loop
+ // after 100 iterations, just in case the corruption isn't caught by the first
+ // check.
+
+ for ( header = WSA_CMSG_FIRSTHDR( &wmsg ); header; header = WSA_CMSG_NXTHDR( &wmsg, header ) )
+ {
+ if ( ( header != last ) && ( ++count < 100 ) )
+ {
+ last = header;
+
+ if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
+ {
+ IN_PKTINFO * ipv4PacketInfo;
+
+ ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
+
+ if ( udpSock->ifd != NULL )
+ {
+ require_action( ipv4PacketInfo->ipi_ifindex == udpSock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
+ }
+
+ dstAddr.type = mDNSAddrType_IPv4;
+ dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr;
+ }
+ else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
+ {
+ IN6_PKTINFO * ipv6PacketInfo;
+
+ ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
+
+ if ( udpSock->ifd != NULL )
+ {
+ require_action( ipv6PacketInfo->ipi6_ifindex == ( udpSock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
+ }
+
+ dstAddr.type = mDNSAddrType_IPv6;
+ dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
+ }
+ }
+ else
+ {
+ static BOOL loggedMessage = FALSE;
+
+ if ( !loggedMessage )
+ {
+ LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
+ loggedMessage = TRUE;
+ }
+
+ break;
+ }
+ }
+ }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+ dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", num );
+ dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &srcAddr, ntohs( srcPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &dstAddr, ntohs( dstPort.NotAnInteger ) );
+
+ if ( udpSock->ifd != NULL )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &udpSock->ifd->interfaceInfo.ip, udpSock->ifd->index );
+ }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
+
+ iid = udpSock->ifd ? udpSock->ifd->interfaceInfo.InterfaceID : NULL;
+ end = ( (mDNSu8 *) &udpSock->packet ) + num;
+
+ mDNSCoreReceive( udpSock->m, &udpSock->packet, end, &srcAddr, srcPort, &dstAddr, dstPort, iid );
+
+exit:
+
+ return;
+}
+
+
+//===========================================================================================================================
+// InterfaceListDidChange
+//===========================================================================================================================
+void InterfaceListDidChange( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" );
+ check( inMDNS );
+
+ // Tear down the existing interfaces and set up new ones using the new IP info.
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = SetupInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = uDNS_SetupDNSConfig( inMDNS );
+ check_noerr( err );
+
+ // Inform clients of the change.
+
+ mDNS_ConfigChanged(inMDNS);
+
+ // Force mDNS to update.
+
+ mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this
+}
+
+
+//===========================================================================================================================
+// ComputerDescriptionDidChange
+//===========================================================================================================================
+void ComputerDescriptionDidChange( mDNS * const inMDNS )
+{
+ dlog( kDebugLevelInfo, DEBUG_NAME "computer description has changed\n" );
+ check( inMDNS );
+
+ // redo the names
+ SetupNiceName( inMDNS );
+}
+
+
+//===========================================================================================================================
+// TCPIPConfigDidChange
+//===========================================================================================================================
+void TCPIPConfigDidChange( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "TCP/IP config has changed\n" );
+ check( inMDNS );
+
+ err = uDNS_SetupDNSConfig( inMDNS );
+ check_noerr( err );
+}
+
+
+//===========================================================================================================================
+// DynDNSConfigDidChange
+//===========================================================================================================================
+void DynDNSConfigDidChange( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "DynDNS config has changed\n" );
+ check( inMDNS );
+
+ SetDomainSecrets( inMDNS );
+
+ err = uDNS_SetupDNSConfig( inMDNS );
+ check_noerr( err );
+}
+
+
+//===========================================================================================================================
+// FileSharingDidChange
+//===========================================================================================================================
+void FileSharingDidChange( mDNS * const inMDNS )
+{
+ dlog( kDebugLevelInfo, DEBUG_NAME "File shares has changed\n" );
+ check( inMDNS );
+
+ CheckFileShares( inMDNS );
+}
+
+
+//===========================================================================================================================
+// FilewallDidChange
+//===========================================================================================================================
+void FirewallDidChange( mDNS * const inMDNS )
+{
+ dlog( kDebugLevelInfo, DEBUG_NAME "Firewall has changed\n" );
+ check( inMDNS );
+
+ CheckFileShares( inMDNS );
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+// getifaddrs
+//===========================================================================================================================
+
+mDNSlocal int getifaddrs( struct ifaddrs **outAddrs )
+{
+ int err;
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+
+ // Try to the load the GetAdaptersAddresses function from the IP Helpers DLL. This API is only available on Windows
+ // XP or later. Looking up the symbol at runtime allows the code to still work on older systems without that API.
+
+ if( !gIPHelperLibraryInstance )
+ {
+ gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
+ if( gIPHelperLibraryInstance )
+ {
+ gGetAdaptersAddressesFunctionPtr =
+ (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" );
+ if( !gGetAdaptersAddressesFunctionPtr )
+ {
+ BOOL ok;
+
+ ok = FreeLibrary( gIPHelperLibraryInstance );
+ check_translated_errno( ok, GetLastError(), kUnknownErr );
+ gIPHelperLibraryInstance = NULL;
+ }
+ }
+ }
+
+ // Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code.
+ // <rdar://problem/4278934> Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 fails
+ // <rdar://problem/6145913> Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 returns no addrs
+
+ if( !gGetAdaptersAddressesFunctionPtr || ( ( ( err = getifaddrs_ipv6( outAddrs ) ) != mStatus_NoError ) || ( ( outAddrs != NULL ) && ( *outAddrs == NULL ) ) ) )
+ {
+ err = getifaddrs_ipv4( outAddrs );
+ require_noerr( err, exit );
+ }
+
+#else
+
+ err = getifaddrs_ipv4( outAddrs );
+ require_noerr( err, exit );
+
+#endif
+
+exit:
+ return( err );
+}
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+//===========================================================================================================================
+// getifaddrs_ipv6
+//===========================================================================================================================
+
+mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs )
+{
+ DWORD err;
+ int i;
+ DWORD flags;
+ struct ifaddrs * head;
+ struct ifaddrs ** next;
+ IP_ADAPTER_ADDRESSES * iaaList;
+ ULONG iaaListSize;
+ IP_ADAPTER_ADDRESSES * iaa;
+ size_t size;
+ struct ifaddrs * ifa;
+
+ check( gGetAdaptersAddressesFunctionPtr );
+
+ head = NULL;
+ next = &head;
+ iaaList = NULL;
+
+ // Get the list of interfaces. The first call gets the size and the second call gets the actual data.
+ // This loops to handle the case where the interface changes in the window after getting the size, but before the
+ // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong.
+
+ flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
+ i = 0;
+ for( ;; )
+ {
+ iaaListSize = 0;
+ err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize );
+ check( err == ERROR_BUFFER_OVERFLOW );
+ check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) );
+
+ iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize );
+ require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY );
+
+ err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize );
+ if( err == ERROR_SUCCESS ) break;
+
+ free( iaaList );
+ iaaList = NULL;
+ ++i;
+ require( i < 100, exit );
+ dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err );
+ }
+
+ for( iaa = iaaList; iaa; iaa = iaa->Next )
+ {
+ int addrIndex;
+ IP_ADAPTER_UNICAST_ADDRESS * addr;
+ DWORD ipv6IfIndex;
+ IP_ADAPTER_PREFIX * firstPrefix;
+
+ if( iaa->IfIndex > 0xFFFFFF )
+ {
+ dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv4 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->IfIndex );
+ }
+ if( iaa->Ipv6IfIndex > 0xFF )
+ {
+ dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv6 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->Ipv6IfIndex );
+ }
+
+ // For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the
+ // following code to crash when iterating through the prefix list. This seems
+ // to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host.
+ // This shouldn't happen according to Microsoft docs which states:
+ //
+ // "Ipv6IfIndex contains 0 if IPv6 is not available on the interface."
+ //
+ // So the data structure seems to be corrupted when we return from
+ // GetAdaptersAddresses(). The bug seems to occur when iaa->Length <
+ // sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually
+ // modify iaa to have the correct values.
+
+ if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) )
+ {
+ ipv6IfIndex = iaa->Ipv6IfIndex;
+ firstPrefix = iaa->FirstPrefix;
+ }
+ else
+ {
+ ipv6IfIndex = 0;
+ firstPrefix = NULL;
+ }
+
+ // Skip pseudo and tunnel interfaces.
+
+ if( ( ( ipv6IfIndex == 1 ) && ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) ) || ( iaa->IfType == IF_TYPE_TUNNEL ) )
+ {
+ continue;
+ }
+
+ // Add each address as a separate interface to emulate the way getifaddrs works.
+
+ for( addrIndex = 0, addr = iaa->FirstUnicastAddress; addr; ++addrIndex, addr = addr->Next )
+ {
+ int family;
+ IP_ADAPTER_PREFIX * prefix;
+ uint32_t ipv4Index;
+ struct sockaddr_in ipv4Netmask;
+
+ family = addr->Address.lpSockaddr->sa_family;
+ if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
+
+ // <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8
+ // Seems as if the problem here is a buggy implementation of some network interface
+ // driver. It is reporting that is has a link-local address when it is actually
+ // disconnected. This was causing a problem in AddressToIndexAndMask.
+ // The solution is to call AddressToIndexAndMask first, and if unable to lookup
+ // the address, to ignore that address.
+
+ ipv4Index = 0;
+ memset( &ipv4Netmask, 0, sizeof( ipv4Netmask ) );
+
+ if ( family == AF_INET )
+ {
+ err = AddressToIndexAndMask( addr->Address.lpSockaddr, &ipv4Index, ( struct sockaddr* ) &ipv4Netmask );
+
+ if ( err )
+ {
+ err = 0;
+ continue;
+ }
+ }
+
+ ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+ require_action( ifa, exit, err = WSAENOBUFS );
+
+ *next = ifa;
+ next = &ifa->ifa_next;
+
+ // Get the name.
+
+ size = strlen( iaa->AdapterName ) + 1;
+ ifa->ifa_name = (char *) malloc( size );
+ require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_name, iaa->AdapterName, size );
+
+ // Get interface flags.
+
+ ifa->ifa_flags = 0;
+ if( iaa->OperStatus == IfOperStatusUp ) ifa->ifa_flags |= IFF_UP;
+ if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK ) ifa->ifa_flags |= IFF_LOOPBACK;
+ else if ( IsPointToPoint( addr ) ) ifa->ifa_flags |= IFF_POINTTOPOINT;
+ if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) ) ifa->ifa_flags |= IFF_MULTICAST;
+
+
+ // <rdar://problem/4045657> Interface index being returned is 512
+ //
+ // Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes.
+ // This code used to shift the IPv4 index up to ensure uniqueness between
+ // it and IPv6 indexes. Although this worked, it was somewhat confusing to developers, who
+ // then see interface indexes passed back that don't correspond to anything
+ // that is seen in Win32 APIs or command line tools like "route". As a relatively
+ // small percentage of developers are actively using IPv6, it seems to
+ // make sense to make our use of IPv4 as confusion free as possible.
+ // So now, IPv6 interface indexes will be shifted up by a
+ // constant value which will serve to uniquely identify them, and we will
+ // leave IPv4 interface indexes unmodified.
+
+ switch( family )
+ {
+ case AF_INET: ifa->ifa_extra.index = iaa->IfIndex; break;
+ case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex + kIPv6IfIndexBase; break;
+ default: break;
+ }
+
+ // Get lease lifetime
+
+ if ( ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) && ( addr->LeaseLifetime != 0 ) && ( addr->ValidLifetime != 0xFFFFFFFF ) )
+ {
+ ifa->ifa_dhcpEnabled = TRUE;
+ ifa->ifa_dhcpLeaseExpires = time( NULL ) + addr->ValidLifetime;
+ }
+ else
+ {
+ ifa->ifa_dhcpEnabled = FALSE;
+ ifa->ifa_dhcpLeaseExpires = 0;
+ }
+
+ if ( iaa->PhysicalAddressLength == sizeof( ifa->ifa_physaddr ) )
+ {
+ memcpy( ifa->ifa_physaddr, iaa->PhysicalAddress, iaa->PhysicalAddressLength );
+ }
+
+ // Because we don't get notified of womp changes, we're going to just assume
+ // that all wired interfaces have it enabled. Before we go to sleep, we'll check
+ // if the interface actually supports it, and update mDNS->SystemWakeOnLANEnabled
+ // accordingly
+
+ ifa->ifa_womp = ( iaa->IfType == IF_TYPE_ETHERNET_CSMACD ) ? mDNStrue : mDNSfalse;
+
+ // Get address.
+
+ switch( family )
+ {
+ case AF_INET:
+ case AF_INET6:
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, (size_t) addr->Address.iSockaddrLength );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, addr->Address.lpSockaddr, (size_t) addr->Address.iSockaddrLength );
+ break;
+
+ default:
+ break;
+ }
+ check( ifa->ifa_addr );
+
+ // Get subnet mask (IPv4)/link prefix (IPv6). It is specified as a bit length (e.g. 24 for 255.255.255.0).
+
+ switch ( family )
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in * sa4;
+
+ sa4 = (struct sockaddr_in *) calloc( 1, sizeof( *sa4 ) );
+ require_action( sa4, exit, err = WSAENOBUFS );
+ sa4->sin_family = AF_INET;
+ sa4->sin_addr.s_addr = ipv4Netmask.sin_addr.s_addr;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv4 mask = %s\n", __ROUTINE__, inet_ntoa( sa4->sin_addr ) );
+ ifa->ifa_netmask = (struct sockaddr *) sa4;
+ break;
+ }
+
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sa6;
+ char buf[ 256 ] = { 0 };
+ DWORD buflen = sizeof( buf );
+
+ sa6 = (struct sockaddr_in6 *) calloc( 1, sizeof( *sa6 ) );
+ require_action( sa6, exit, err = WSAENOBUFS );
+ sa6->sin6_family = AF_INET6;
+ memset( sa6->sin6_addr.s6_addr, 0xFF, sizeof( sa6->sin6_addr.s6_addr ) );
+ ifa->ifa_netmask = (struct sockaddr *) sa6;
+
+ for ( prefix = firstPrefix; prefix; prefix = prefix->Next )
+ {
+ IN6_ADDR mask;
+ IN6_ADDR maskedAddr;
+ int maskIndex;
+ DWORD len;
+
+ // According to MSDN:
+ // "On Windows Vista and later, the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member
+ // include three IP adapter prefixes for each IP address assigned to the adapter. These include the host IP address prefix,
+ // the subnet IP address prefix, and the subnet broadcast IP address prefix.
+ // In addition, for each adapter there is a multicast address prefix and a broadcast address prefix.
+ // On Windows XP with SP1 and later prior to Windows Vista, the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member
+ // include only a single IP adapter prefix for each IP address assigned to the adapter."
+
+ // We're only interested in the subnet IP address prefix. We'll determine if the prefix is the
+ // subnet prefix by masking our address with a mask (computed from the prefix length) and see if that is the same
+ // as the prefix address.
+
+ if ( ( prefix->PrefixLength == 0 ) ||
+ ( prefix->PrefixLength > 128 ) ||
+ ( addr->Address.iSockaddrLength != prefix->Address.iSockaddrLength ) ||
+ ( memcmp( addr->Address.lpSockaddr, prefix->Address.lpSockaddr, addr->Address.iSockaddrLength ) == 0 ) )
+ {
+ continue;
+ }
+
+ // Compute the mask
+
+ memset( mask.s6_addr, 0, sizeof( mask.s6_addr ) );
+
+ for ( len = (int) prefix->PrefixLength, maskIndex = 0; len > 0; len -= 8 )
+ {
+ uint8_t maskByte = ( len >= 8 ) ? 0xFF : (uint8_t)( ( 0xFFU << ( 8 - len ) ) & 0xFFU );
+ mask.s6_addr[ maskIndex++ ] = maskByte;
+ }
+
+ // Apply the mask
+
+ for ( i = 0; i < 16; i++ )
+ {
+ maskedAddr.s6_addr[ i ] = ( ( struct sockaddr_in6* ) addr->Address.lpSockaddr )->sin6_addr.s6_addr[ i ] & mask.s6_addr[ i ];
+ }
+
+ // Compare
+
+ if ( memcmp( ( ( struct sockaddr_in6* ) prefix->Address.lpSockaddr )->sin6_addr.s6_addr, maskedAddr.s6_addr, sizeof( maskedAddr.s6_addr ) ) == 0 )
+ {
+ memcpy( sa6->sin6_addr.s6_addr, mask.s6_addr, sizeof( mask.s6_addr ) );
+ break;
+ }
+ }
+
+ WSAAddressToStringA( ( LPSOCKADDR ) sa6, sizeof( struct sockaddr_in6 ), NULL, buf, &buflen );
+ dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv6 mask = %s\n", __ROUTINE__, buf );
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Success!
+
+ if( outAddrs )
+ {
+ *outAddrs = head;
+ head = NULL;
+ }
+ err = ERROR_SUCCESS;
+
+exit:
+ if( head )
+ {
+ freeifaddrs( head );
+ }
+ if( iaaList )
+ {
+ free( iaaList );
+ }
+ return( (int) err );
+}
+
+#endif // MDNS_WINDOWS_USE_IPV6_IF_ADDRS
+
+//===========================================================================================================================
+// getifaddrs_ipv4
+//===========================================================================================================================
+
+mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs )
+{
+ int err;
+ SOCKET sock;
+ DWORD size;
+ DWORD actualSize;
+ INTERFACE_INFO * buffer;
+ INTERFACE_INFO * tempBuffer;
+ INTERFACE_INFO * ifInfo;
+ int n;
+ int i;
+ struct ifaddrs * head;
+ struct ifaddrs ** next;
+ struct ifaddrs * ifa;
+
+ sock = INVALID_SOCKET;
+ buffer = NULL;
+ head = NULL;
+ next = &head;
+
+ // Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a
+ // way to determine the size of the interface list beforehand, we have to start with an initial size guess and
+ // call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety.
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ n = 0;
+ size = 16 * sizeof( INTERFACE_INFO );
+ for( ;; )
+ {
+ tempBuffer = (INTERFACE_INFO *) realloc( buffer, size );
+ require_action( tempBuffer, exit, err = WSAENOBUFS );
+ buffer = tempBuffer;
+
+ err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL );
+ if( err == 0 )
+ {
+ break;
+ }
+
+ ++n;
+ require_action( n < 100, exit, err = WSAEADDRNOTAVAIL );
+
+ size += ( 16 * sizeof( INTERFACE_INFO ) );
+ }
+ check( actualSize <= size );
+ check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 );
+ n = (int)( actualSize / sizeof( INTERFACE_INFO ) );
+
+ // Process the raw interface list and build a linked list of IPv4 interfaces.
+
+ for( i = 0; i < n; ++i )
+ {
+ uint32_t ifIndex;
+ struct sockaddr_in netmask;
+
+ ifInfo = &buffer[ i ];
+ if( ifInfo->iiAddress.Address.sa_family != AF_INET )
+ {
+ continue;
+ }
+
+ // <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8
+ // See comment in getifaddrs_ipv6
+
+ ifIndex = 0;
+ memset( &netmask, 0, sizeof( netmask ) );
+ err = AddressToIndexAndMask( ( struct sockaddr* ) &ifInfo->iiAddress.AddressIn, &ifIndex, ( struct sockaddr* ) &netmask );
+
+ if ( err )
+ {
+ continue;
+ }
+
+ ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+ require_action( ifa, exit, err = WSAENOBUFS );
+
+ *next = ifa;
+ next = &ifa->ifa_next;
+
+ // Get the name.
+
+ ifa->ifa_name = (char *) malloc( 16 );
+ require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+ sprintf( ifa->ifa_name, "%d", i + 1 );
+
+ // Get interface flags.
+
+ ifa->ifa_flags = (u_int) ifInfo->iiFlags;
+
+ // Get addresses.
+
+ if ( ifInfo->iiAddress.Address.sa_family == AF_INET )
+ {
+ struct sockaddr_in * sa4;
+
+ sa4 = &ifInfo->iiAddress.AddressIn;
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) );
+
+ ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) );
+ require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS );
+
+ // <rdar://problem/4076478> Service won't start on Win2K. The address
+ // family field was not being initialized.
+
+ ifa->ifa_netmask->sa_family = AF_INET;
+ ( ( struct sockaddr_in* ) ifa->ifa_netmask )->sin_addr = netmask.sin_addr;
+ ifa->ifa_extra.index = ifIndex;
+ }
+ else
+ {
+ // Emulate an interface index.
+
+ ifa->ifa_extra.index = (uint32_t)( i + 1 );
+ }
+ }
+
+ // Success!
+
+ if( outAddrs )
+ {
+ *outAddrs = head;
+ head = NULL;
+ }
+ err = 0;
+
+exit:
+
+ if( head )
+ {
+ freeifaddrs( head );
+ }
+ if( buffer )
+ {
+ free( buffer );
+ }
+ if( sock != INVALID_SOCKET )
+ {
+ closesocket( sock );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// freeifaddrs
+//===========================================================================================================================
+
+mDNSlocal void freeifaddrs( struct ifaddrs *inIFAs )
+{
+ struct ifaddrs * p;
+ struct ifaddrs * q;
+
+ // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields.
+
+ for( p = inIFAs; p; p = q )
+ {
+ q = p->ifa_next;
+
+ if( p->ifa_name )
+ {
+ free( p->ifa_name );
+ p->ifa_name = NULL;
+ }
+ if( p->ifa_addr )
+ {
+ free( p->ifa_addr );
+ p->ifa_addr = NULL;
+ }
+ if( p->ifa_netmask )
+ {
+ free( p->ifa_netmask );
+ p->ifa_netmask = NULL;
+ }
+ if( p->ifa_broadaddr )
+ {
+ free( p->ifa_broadaddr );
+ p->ifa_broadaddr = NULL;
+ }
+ if( p->ifa_dstaddr )
+ {
+ free( p->ifa_dstaddr );
+ p->ifa_dstaddr = NULL;
+ }
+ if( p->ifa_data )
+ {
+ free( p->ifa_data );
+ p->ifa_data = NULL;
+ }
+ free( p );
+ }
+}
+
+
+//===========================================================================================================================
+// GetPrimaryInterface
+//===========================================================================================================================
+
+mDNSlocal DWORD
+GetPrimaryInterface()
+{
+ PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
+ DWORD dwSize = 0;
+ BOOL bOrder = FALSE;
+ OSStatus err;
+ DWORD index = 0;
+ DWORD metric = 0;
+ unsigned long int i;
+
+ // Find out how big our buffer needs to be.
+
+ err = GetIpForwardTable(NULL, &dwSize, bOrder);
+ require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
+
+ // Allocate the memory for the table
+
+ pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
+ require_action( pIpForwardTable, exit, err = kNoMemoryErr );
+
+ // Now get the table.
+
+ err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
+ require_noerr( err, exit );
+
+
+ // Search for the row in the table we want.
+
+ for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
+ {
+ // Look for a default route
+
+ if ( pIpForwardTable->table[i].dwForwardDest == 0 )
+ {
+ if ( index && ( pIpForwardTable->table[i].dwForwardMetric1 >= metric ) )
+ {
+ continue;
+ }
+
+ index = pIpForwardTable->table[i].dwForwardIfIndex;
+ metric = pIpForwardTable->table[i].dwForwardMetric1;
+ }
+ }
+
+exit:
+
+ if ( pIpForwardTable != NULL )
+ {
+ free( pIpForwardTable );
+ }
+
+ return index;
+}
+
+
+//===========================================================================================================================
+// AddressToIndexAndMask
+//===========================================================================================================================
+
+mDNSlocal mStatus
+AddressToIndexAndMask( struct sockaddr * addr, uint32_t * ifIndex, struct sockaddr * mask )
+{
+ // Before calling AddIPAddress we use GetIpAddrTable to get
+ // an adapter to which we can add the IP.
+
+ PMIB_IPADDRTABLE pIPAddrTable = NULL;
+ DWORD dwSize = 0;
+ mStatus err = mStatus_UnknownErr;
+ DWORD i;
+
+ // For now, this is only for IPv4 addresses. That is why we can safely cast
+ // addr's to sockaddr_in.
+
+ require_action( addr->sa_family == AF_INET, exit, err = mStatus_UnknownErr );
+
+ // Make an initial call to GetIpAddrTable to get the
+ // necessary size into the dwSize variable
+
+ for ( i = 0; i < 100; i++ )
+ {
+ err = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+
+ if ( err != ERROR_INSUFFICIENT_BUFFER )
+ {
+ break;
+ }
+
+ pIPAddrTable = (MIB_IPADDRTABLE *) realloc( pIPAddrTable, dwSize );
+ require_action( pIPAddrTable, exit, err = WSAENOBUFS );
+ }
+
+ require_noerr( err, exit );
+ err = mStatus_UnknownErr;
+
+ for ( i = 0; i < pIPAddrTable->dwNumEntries; i++ )
+ {
+ if ( ( ( struct sockaddr_in* ) addr )->sin_addr.s_addr == pIPAddrTable->table[i].dwAddr )
+ {
+ *ifIndex = pIPAddrTable->table[i].dwIndex;
+ ( ( struct sockaddr_in*) mask )->sin_addr.s_addr = pIPAddrTable->table[i].dwMask;
+ err = mStatus_NoError;
+ break;
+ }
+ }
+
+exit:
+
+ if ( pIPAddrTable )
+ {
+ free( pIPAddrTable );
+ }
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// CanReceiveUnicast
+//===========================================================================================================================
+
+mDNSlocal mDNSBool CanReceiveUnicast( void )
+{
+ mDNSBool ok;
+ SocketRef sock;
+ struct sockaddr_in addr;
+
+ // Try to bind to the port without the SO_REUSEADDR option to test if someone else has already bound to it.
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ check_translated_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+ ok = IsValidSocket( sock );
+ if( ok )
+ {
+ mDNSPlatformMemZero( &addr, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = MulticastDNSPort.NotAnInteger;
+ addr.sin_addr.s_addr = htonl( INADDR_ANY );
+
+ ok = ( bind( sock, (struct sockaddr *) &addr, sizeof( addr ) ) == 0 );
+ close_compat( sock );
+ }
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Unicast UDP responses %s\n", ok ? "okay" : "*not allowed*" );
+ return( ok );
+}
+
+
+//===========================================================================================================================
+// IsPointToPoint
+//===========================================================================================================================
+
+mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr )
+{
+ struct ifaddrs * addrs = NULL;
+ struct ifaddrs * p = NULL;
+ OSStatus err;
+ mDNSBool ret = mDNSfalse;
+
+ // For now, only works for IPv4 interfaces
+
+ if ( addr->Address.lpSockaddr->sa_family == AF_INET )
+ {
+ // The getifaddrs_ipv4 call will give us correct information regarding IFF_POINTTOPOINT flags.
+
+ err = getifaddrs_ipv4( &addrs );
+ require_noerr( err, exit );
+
+ for ( p = addrs; p; p = p->ifa_next )
+ {
+ if ( ( addr->Address.lpSockaddr->sa_family == p->ifa_addr->sa_family ) &&
+ ( ( ( struct sockaddr_in* ) addr->Address.lpSockaddr )->sin_addr.s_addr == ( ( struct sockaddr_in* ) p->ifa_addr )->sin_addr.s_addr ) )
+ {
+ ret = ( p->ifa_flags & IFF_POINTTOPOINT ) ? mDNStrue : mDNSfalse;
+ break;
+ }
+ }
+ }
+
+exit:
+
+ if ( addrs )
+ {
+ freeifaddrs( addrs );
+ }
+
+ return ret;
+}
+
+
+//===========================================================================================================================
+// GetWindowsVersionString
+//===========================================================================================================================
+
+mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize )
+{
+#if( !defined( VER_PLATFORM_WIN32_CE ) )
+ #define VER_PLATFORM_WIN32_CE 3
+#endif
+
+ OSStatus err;
+ OSVERSIONINFO osInfo;
+ BOOL ok;
+ const char * versionString;
+ DWORD platformID;
+ DWORD majorVersion;
+ DWORD minorVersion;
+ DWORD buildNumber;
+
+ versionString = "unknown Windows version";
+
+ osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
+ ok = GetVersionEx( &osInfo );
+ err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ platformID = osInfo.dwPlatformId;
+ majorVersion = osInfo.dwMajorVersion;
+ minorVersion = osInfo.dwMinorVersion;
+ buildNumber = osInfo.dwBuildNumber & 0xFFFF;
+
+ if( ( platformID == VER_PLATFORM_WIN32_WINDOWS ) && ( majorVersion == 4 ) )
+ {
+ if( ( minorVersion < 10 ) && ( buildNumber == 950 ) )
+ {
+ versionString = "Windows 95";
+ }
+ else if( ( minorVersion < 10 ) && ( ( buildNumber > 950 ) && ( buildNumber <= 1080 ) ) )
+ {
+ versionString = "Windows 95 SP1";
+ }
+ else if( ( minorVersion < 10 ) && ( buildNumber > 1080 ) )
+ {
+ versionString = "Windows 95 OSR2";
+ }
+ else if( ( minorVersion == 10 ) && ( buildNumber == 1998 ) )
+ {
+ versionString = "Windows 98";
+ }
+ else if( ( minorVersion == 10 ) && ( ( buildNumber > 1998 ) && ( buildNumber < 2183 ) ) )
+ {
+ versionString = "Windows 98 SP1";
+ }
+ else if( ( minorVersion == 10 ) && ( buildNumber >= 2183 ) )
+ {
+ versionString = "Windows 98 SE";
+ }
+ else if( minorVersion == 90 )
+ {
+ versionString = "Windows ME";
+ }
+ }
+ else if( platformID == VER_PLATFORM_WIN32_NT )
+ {
+ if( ( majorVersion == 3 ) && ( minorVersion == 51 ) )
+ {
+ versionString = "Windows NT 3.51";
+ }
+ else if( ( majorVersion == 4 ) && ( minorVersion == 0 ) )
+ {
+ versionString = "Windows NT 4";
+ }
+ else if( ( majorVersion == 5 ) && ( minorVersion == 0 ) )
+ {
+ versionString = "Windows 2000";
+ }
+ else if( ( majorVersion == 5 ) && ( minorVersion == 1 ) )
+ {
+ versionString = "Windows XP";
+ }
+ else if( ( majorVersion == 5 ) && ( minorVersion == 2 ) )
+ {
+ versionString = "Windows Server 2003";
+ }
+ }
+ else if( platformID == VER_PLATFORM_WIN32_CE )
+ {
+ versionString = "Windows CE";
+ }
+
+exit:
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ inBufferSize -= 1;
+ strncpy( inBuffer, versionString, inBufferSize );
+ inBuffer[ inBufferSize ] = '\0';
+ }
+ return( err );
+}
+
+
+//===========================================================================================================================
+// RegQueryString
+//===========================================================================================================================
+
+mDNSlocal mStatus
+RegQueryString( HKEY key, LPCSTR valueName, LPSTR * string, DWORD * stringLen, DWORD * enabled )
+{
+ DWORD type;
+ int i;
+ mStatus err;
+
+ *stringLen = MAX_ESCAPED_DOMAIN_NAME;
+ *string = NULL;
+ i = 0;
+
+ do
+ {
+ if ( *string )
+ {
+ free( *string );
+ }
+
+ *string = (char*) malloc( *stringLen );
+ require_action( *string, exit, err = mStatus_NoMemoryErr );
+
+ err = RegQueryValueExA( key, valueName, 0, &type, (LPBYTE) *string, stringLen );
+
+ i++;
+ }
+ while ( ( err == ERROR_MORE_DATA ) && ( i < 100 ) );
+
+ require_noerr_quiet( err, exit );
+
+ if ( enabled )
+ {
+ DWORD dwSize = sizeof( DWORD );
+
+ err = RegQueryValueEx( key, TEXT("Enabled"), NULL, NULL, (LPBYTE) enabled, &dwSize );
+ check_noerr( err );
+
+ err = kNoErr;
+ }
+
+exit:
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// StringToAddress
+//===========================================================================================================================
+
+mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string )
+{
+ struct sockaddr_in6 sa6;
+ struct sockaddr_in sa4;
+ INT dwSize;
+ mStatus err;
+
+ sa6.sin6_family = AF_INET6;
+ dwSize = sizeof( sa6 );
+
+ err = WSAStringToAddressA( string, AF_INET6, NULL, (struct sockaddr*) &sa6, &dwSize );
+
+ if ( err == mStatus_NoError )
+ {
+ err = SetupAddr( ip, (struct sockaddr*) &sa6 );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ sa4.sin_family = AF_INET;
+ dwSize = sizeof( sa4 );
+
+ err = WSAStringToAddressA( string, AF_INET, NULL, (struct sockaddr*) &sa4, &dwSize );
+ err = translate_errno( err == 0, WSAGetLastError(), kUnknownErr );
+ require_noerr( err, exit );
+
+ err = SetupAddr( ip, (struct sockaddr*) &sa4 );
+ require_noerr( err, exit );
+ }
+
+exit:
+
+ return err;
+}
+
+
+//===========================================================================================================================
+// myGetIfAddrs
+//===========================================================================================================================
+
+mDNSlocal struct ifaddrs*
+myGetIfAddrs(int refresh)
+{
+ static struct ifaddrs *ifa = NULL;
+
+ if (refresh && ifa)
+ {
+ freeifaddrs(ifa);
+ ifa = NULL;
+ }
+
+ if (ifa == NULL)
+ {
+ getifaddrs(&ifa);
+ }
+
+ return ifa;
+}
+
+
+//===========================================================================================================================
+// TCHARtoUTF8
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize )
+{
+#if( defined( UNICODE ) || defined( _UNICODE ) )
+ OSStatus err;
+ int len;
+
+ len = WideCharToMultiByte( CP_UTF8, 0, inString, -1, inBuffer, (int) inBufferSize, NULL, NULL );
+ err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+exit:
+ return( err );
+#else
+ return( WindowsLatin1toUTF8( inString, inBuffer, inBufferSize ) );
+#endif
+}
+
+
+//===========================================================================================================================
+// WindowsLatin1toUTF8
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize )
+{
+ OSStatus err;
+ WCHAR * utf16;
+ int len;
+
+ utf16 = NULL;
+
+ // Windows doesn't support going directly from Latin-1 to UTF-8 so we have to go from Latin-1 to UTF-16 first.
+
+ len = MultiByteToWideChar( CP_ACP, 0, inString, -1, NULL, 0 );
+ err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ utf16 = (WCHAR *) malloc( len * sizeof( *utf16 ) );
+ require_action( utf16, exit, err = kNoMemoryErr );
+
+ len = MultiByteToWideChar( CP_ACP, 0, inString, -1, utf16, len );
+ err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+ // Now convert the temporary UTF-16 to UTF-8.
+
+ len = WideCharToMultiByte( CP_UTF8, 0, utf16, -1, inBuffer, (int) inBufferSize, NULL, NULL );
+ err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+ require_noerr( err, exit );
+
+exit:
+ if( utf16 ) free( utf16 );
+ return( err );
+}
+
+
+//===========================================================================================================================
+// TCPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+TCPCloseSocket( TCPSocket * sock )
+{
+ dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd );
+
+ if ( sock->fd != INVALID_SOCKET )
+ {
+ closesocket( sock->fd );
+ sock->fd = INVALID_SOCKET;
+ }
+}
+
+
+//===========================================================================================================================
+// UDPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+UDPCloseSocket( UDPSocket * sock )
+{
+ dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd );
+
+ if ( sock->fd != INVALID_SOCKET )
+ {
+ mDNSPollUnregisterSocket( sock->fd );
+ closesocket( sock->fd );
+ sock->fd = INVALID_SOCKET;
+ }
+}
+
+
+//===========================================================================================================================
+// SetupAddr
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa)
+ {
+ if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); }
+
+ if (sa->sa_family == AF_INET)
+ {
+ struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa;
+ ip->type = mDNSAddrType_IPv4;
+ ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr;
+ return(mStatus_NoError);
+ }
+
+ if (sa->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa;
+ ip->type = mDNSAddrType_IPv6;
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.u.Word[1] = 0;
+ ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr;
+ return(mStatus_NoError);
+ }
+
+ LogMsg("SetupAddr invalid sa_family %d", sa->sa_family);
+ return(mStatus_Invalid);
+ }
+
+
+mDNSlocal void GetDDNSFQDN( domainname *const fqdn )
+{
+ LPSTR name = NULL;
+ DWORD dwSize;
+ DWORD enabled;
+ HKEY key = NULL;
+ OSStatus err;
+
+ check( fqdn );
+
+ // Initialize
+
+ fqdn->c[0] = '\0';
+
+ // Get info from Bonjour registry key
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key );
+ require_noerr( err, exit );
+
+ err = RegQueryString( key, "", &name, &dwSize, &enabled );
+ if ( !err && ( name[0] != '\0' ) && enabled )
+ {
+ if ( !MakeDomainNameFromDNSNameString( fqdn, name ) || !fqdn->c[0] )
+ {
+ dlog( kDebugLevelError, "bad DDNS host name in registry: %s", name[0] ? name : "(unknown)");
+ }
+ }
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ key = NULL;
+ }
+
+ if ( name )
+ {
+ free( name );
+ name = NULL;
+ }
+}
+
+
+#ifdef UNICODE
+mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey )
+#else
+mDNSlocal void GetDDNSConfig( DNameListElem ** domains, LPCSTR lpSubKey )
+#endif
+{
+ char subKeyName[kRegistryMaxKeyLength + 1];
+ DWORD cSubKeys = 0;
+ DWORD cbMaxSubKey;
+ DWORD cchMaxClass;
+ DWORD dwSize;
+ HKEY key = NULL;
+ HKEY subKey = NULL;
+ domainname dname;
+ DWORD i;
+ OSStatus err;
+
+ check( domains );
+
+ // Initialize
+
+ *domains = NULL;
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, lpSubKey, &key );
+ require_noerr( err, exit );
+
+ // Get information about this node
+
+ err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );
+ require_noerr( err, exit );
+
+ for ( i = 0; i < cSubKeys; i++)
+ {
+ DWORD enabled;
+
+ dwSize = kRegistryMaxKeyLength;
+
+ err = RegEnumKeyExA( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+
+ if ( !err )
+ {
+ err = RegOpenKeyExA( key, subKeyName, 0, KEY_READ, &subKey );
+ require_noerr( err, exit );
+
+ dwSize = sizeof( DWORD );
+ err = RegQueryValueExA( subKey, "Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+
+ if ( !err && ( subKeyName[0] != '\0' ) && enabled )
+ {
+ if ( !MakeDomainNameFromDNSNameString( &dname, subKeyName ) || !dname.c[0] )
+ {
+ dlog( kDebugLevelError, "bad DDNS domain in registry: %s", subKeyName[0] ? subKeyName : "(unknown)");
+ }
+ else
+ {
+ DNameListElem * domain = (DNameListElem*) malloc( sizeof( DNameListElem ) );
+ require_action( domain, exit, err = mStatus_NoMemoryErr );
+
+ AssignDomainName(&domain->name, &dname);
+ domain->next = *domains;
+
+ *domains = domain;
+ }
+ }
+
+ RegCloseKey( subKey );
+ subKey = NULL;
+ }
+ }
+
+exit:
+
+ if ( subKey )
+ {
+ RegCloseKey( subKey );
+ }
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+}
+
+
+mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain )
+{
+ char domainUTF8[ 256 ];
+ DomainAuthInfo *foundInList;
+ DomainAuthInfo *ptr;
+ char outDomain[ 256 ];
+ char outKey[ 256 ];
+ char outSecret[ 256 ];
+ OSStatus err;
+
+ ConvertDomainNameToCString( inDomain, domainUTF8 );
+
+ // If we're able to find a secret for this domain
+
+ if ( LsaGetSecret( domainUTF8, outDomain, sizeof( outDomain ), outKey, sizeof( outKey ), outSecret, sizeof( outSecret ) ) )
+ {
+ domainname domain;
+ domainname key;
+
+ // Tell the core about this secret
+
+ MakeDomainNameFromDNSNameString( &domain, outDomain );
+ MakeDomainNameFromDNSNameString( &key, outKey );
+
+ for (foundInList = m->AuthInfoList; foundInList; foundInList = foundInList->next)
+ if (SameDomainName(&foundInList->domain, &domain ) ) break;
+
+ ptr = foundInList;
+
+ if (!ptr)
+ {
+ ptr = (DomainAuthInfo*)malloc(sizeof(DomainAuthInfo));
+ require_action( ptr, exit, err = mStatus_NoMemoryErr );
+ }
+
+ err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL, NULL, FALSE );
+ require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) );
+
+ debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c);
+ }
+
+exit:
+
+ return;
+}
+
+
+mDNSlocal VOID CALLBACK
+CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue )
+{
+ mDNS * const m = ( mDNS * const ) arg;
+
+ ( void ) dwTimerLowValue;
+ ( void ) dwTimerHighValue;
+
+ CheckFileShares( m );
+}
+
+
+mDNSlocal unsigned __stdcall
+SMBRegistrationThread( void * arg )
+{
+ mDNS * const m = ( mDNS * const ) arg;
+ DNSServiceRef sref = NULL;
+ HANDLE handles[ 3 ];
+ mDNSu8 txtBuf[ 256 ];
+ mDNSu8 * txtPtr;
+ size_t keyLen;
+ size_t valLen;
+ mDNSIPPort port = { { SMBPortAsNumber >> 8, SMBPortAsNumber & 0xFF } };
+ DNSServiceErrorType err;
+
+ DEBUG_UNUSED( arg );
+
+ handles[ 0 ] = gSMBThreadStopEvent;
+ handles[ 1 ] = gSMBThreadRegisterEvent;
+ handles[ 2 ] = gSMBThreadDeregisterEvent;
+
+ memset( txtBuf, 0, sizeof( txtBuf ) );
+ txtPtr = txtBuf;
+ keyLen = strlen( "netbios=" );
+ valLen = strlen( m->p->nbname );
+ require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption
+ *txtPtr++ = ( mDNSu8 ) ( keyLen + valLen );
+ memcpy( txtPtr, "netbios=", keyLen );
+ txtPtr += keyLen;
+ if ( valLen ) { memcpy( txtPtr, m->p->nbname, valLen ); txtPtr += ( mDNSu8 ) valLen; }
+ keyLen = strlen( "domain=" );
+ valLen = strlen( m->p->nbdomain );
+ require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption
+ *txtPtr++ = ( mDNSu8 )( keyLen + valLen );
+ memcpy( txtPtr, "domain=", keyLen );
+ txtPtr += keyLen;
+ if ( valLen ) { memcpy( txtPtr, m->p->nbdomain, valLen ); txtPtr += valLen; }
+
+ for ( ;; )
+ {
+ DWORD ret;
+
+ ret = WaitForMultipleObjects( 3, handles, FALSE, INFINITE );
+
+ if ( ret != WAIT_FAILED )
+ {
+ if ( ret == kSMBStopEvent )
+ {
+ break;
+ }
+ else if ( ret == kSMBRegisterEvent )
+ {
+ err = gDNSServiceRegister( &sref, 0, 0, NULL, "_smb._tcp,_file", NULL, NULL, ( uint16_t ) port.NotAnInteger, ( mDNSu16 )( txtPtr - txtBuf ), txtBuf, NULL, NULL );
+
+ if ( err )
+ {
+ LogMsg( "SMBRegistrationThread: DNSServiceRegister returned %d\n", err );
+ sref = NULL;
+ break;
+ }
+ }
+ else if ( ret == kSMBDeregisterEvent )
+ {
+ if ( sref )
+ {
+ gDNSServiceRefDeallocate( sref );
+ sref = NULL;
+ }
+ }
+ }
+ else
+ {
+ LogMsg( "SMBRegistrationThread: WaitForMultipleObjects returned %d\n", GetLastError() );
+ break;
+ }
+ }
+
+exit:
+
+ if ( sref != NULL )
+ {
+ gDNSServiceRefDeallocate( sref );
+ sref = NULL;
+ }
+
+ SetEvent( gSMBThreadQuitEvent );
+ _endthreadex( 0 );
+ return 0;
+}
+
+
+mDNSlocal void
+CheckFileShares( mDNS * const m )
+{
+ PSHARE_INFO_1 bufPtr = ( PSHARE_INFO_1 ) NULL;
+ DWORD entriesRead = 0;
+ DWORD totalEntries = 0;
+ DWORD resume = 0;
+ mDNSBool advertise = mDNSfalse;
+ mDNSBool fileSharing = mDNSfalse;
+ mDNSBool printSharing = mDNSfalse;
+ HKEY key = NULL;
+ BOOL retry = FALSE;
+ NET_API_STATUS res;
+ mStatus err;
+
+ check( m );
+
+ // Only do this if we're not shutting down
+
+ require_action_quiet( m->AdvertiseLocalAddresses && !m->ShutdownTime, exit, err = kNoErr );
+
+ err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", &key );
+
+ if ( !err )
+ {
+ DWORD dwSize = sizeof( DWORD );
+ RegQueryValueEx( key, L"Advertise", NULL, NULL, (LPBYTE) &advertise, &dwSize );
+ }
+
+ if ( advertise && mDNSIsFileAndPrintSharingEnabled( &retry ) )
+ {
+ dlog( kDebugLevelTrace, DEBUG_NAME "Sharing is enabled\n" );
+
+ res = NetShareEnum( NULL, 1, ( LPBYTE* )&bufPtr, MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, &resume );
+
+ if ( ( res == ERROR_SUCCESS ) || ( res == ERROR_MORE_DATA ) )
+ {
+ PSHARE_INFO_1 p = bufPtr;
+ DWORD i;
+
+ for( i = 0; i < entriesRead; i++ )
+ {
+ // We are only interested if the user is sharing anything other
+ // than the built-in "print$" source
+
+ if ( ( p->shi1_type == STYPE_DISKTREE ) && ( wcscmp( p->shi1_netname, TEXT( "print$" ) ) != 0 ) )
+ {
+ fileSharing = mDNStrue;
+ }
+ else if ( p->shi1_type == STYPE_PRINTQ )
+ {
+ printSharing = mDNStrue;
+ }
+
+ p++;
+ }
+
+ NetApiBufferFree( bufPtr );
+ bufPtr = NULL;
+ retry = FALSE;
+ }
+ else if ( res == NERR_ServerNotStarted )
+ {
+ retry = TRUE;
+ }
+ }
+
+ if ( retry )
+ {
+ __int64 qwTimeout;
+ LARGE_INTEGER liTimeout;
+
+ qwTimeout = -m->p->checkFileSharesTimeout * 10000000;
+ liTimeout.LowPart = ( DWORD )( qwTimeout & 0xFFFFFFFF );
+ liTimeout.HighPart = ( LONG )( qwTimeout >> 32 );
+
+ SetWaitableTimer( m->p->checkFileSharesTimer, &liTimeout, 0, CheckFileSharesProc, m, FALSE );
+ }
+
+ if ( !m->p->smbFileSharing && fileSharing )
+ {
+ if ( !gSMBThread )
+ {
+ if ( !gDNSSDLibrary )
+ {
+ gDNSSDLibrary = LoadLibrary( TEXT( "dnssd.dll" ) );
+ require_action( gDNSSDLibrary, exit, err = GetLastError() );
+ }
+
+ if ( !gDNSServiceRegister )
+ {
+ gDNSServiceRegister = ( DNSServiceRegisterFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRegister" );
+ require_action( gDNSServiceRegister, exit, err = GetLastError() );
+ }
+
+ if ( !gDNSServiceRefDeallocate )
+ {
+ gDNSServiceRefDeallocate = ( DNSServiceRefDeallocateFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRefDeallocate" );
+ require_action( gDNSServiceRefDeallocate, exit, err = GetLastError() );
+ }
+
+ if ( !gSMBThreadRegisterEvent )
+ {
+ gSMBThreadRegisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( gSMBThreadRegisterEvent != NULL, exit, err = GetLastError() );
+ }
+
+ if ( !gSMBThreadDeregisterEvent )
+ {
+ gSMBThreadDeregisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( gSMBThreadDeregisterEvent != NULL, exit, err = GetLastError() );
+ }
+
+ if ( !gSMBThreadStopEvent )
+ {
+ gSMBThreadStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( gSMBThreadStopEvent != NULL, exit, err = GetLastError() );
+ }
+
+ if ( !gSMBThreadQuitEvent )
+ {
+ gSMBThreadQuitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( gSMBThreadQuitEvent != NULL, exit, err = GetLastError() );
+ }
+
+ gSMBThread = ( HANDLE ) _beginthreadex( NULL, 0, SMBRegistrationThread, m, 0, NULL );
+ require_action( gSMBThread != NULL, exit, err = GetLastError() );
+ }
+
+ SetEvent( gSMBThreadRegisterEvent );
+
+ m->p->smbFileSharing = mDNStrue;
+ }
+ else if ( m->p->smbFileSharing && !fileSharing )
+ {
+ dlog( kDebugLevelTrace, DEBUG_NAME "deregistering smb type\n" );
+
+ if ( gSMBThreadDeregisterEvent != NULL )
+ {
+ SetEvent( gSMBThreadDeregisterEvent );
+ }
+
+ m->p->smbFileSharing = mDNSfalse;
+ }
+
+exit:
+
+ if ( key )
+ {
+ RegCloseKey( key );
+ }
+}
+
+
+BOOL
+IsWOMPEnabled( mDNS * const m )
+{
+ BOOL enabled;
+
+ mDNSInterfaceData * ifd;
+
+ enabled = FALSE;
+
+ for( ifd = m->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if ( IsWOMPEnabledForAdapter( ifd->name ) )
+ {
+ enabled = TRUE;
+ break;
+ }
+ }
+
+ return enabled;
+}
+
+
+mDNSlocal mDNSu8
+IsWOMPEnabledForAdapter( const char * adapterName )
+{
+ char fileName[80];
+ NDIS_OID oid;
+ DWORD count;
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ NDIS_PNP_CAPABILITIES * pNPC = NULL;
+ int err;
+ mDNSu8 ok = TRUE;
+
+ require_action( adapterName != NULL, exit, ok = FALSE );
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter: %s\n", adapterName );
+
+ // Construct a device name to pass to CreateFile
+
+ strncpy_s( fileName, sizeof( fileName ), DEVICE_PREFIX, strlen( DEVICE_PREFIX ) );
+ strcat_s( fileName, sizeof( fileName ), adapterName );
+ handle = CreateFileA( fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, INVALID_HANDLE_VALUE );
+ require_action ( handle != INVALID_HANDLE_VALUE, exit, ok = FALSE );
+
+ // We successfully opened the driver, format the IOCTL to pass the driver.
+
+ oid = OID_PNP_CAPABILITIES;
+ pNPC = ( NDIS_PNP_CAPABILITIES * ) malloc( sizeof( NDIS_PNP_CAPABILITIES ) );
+ require_action( pNPC != NULL, exit, ok = FALSE );
+ ok = ( mDNSu8 ) DeviceIoControl( handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof( oid ), pNPC, sizeof( NDIS_PNP_CAPABILITIES ), &count, NULL );
+ err = translate_errno( ok, GetLastError(), kUnknownErr );
+ require_action( !err, exit, ok = FALSE );
+ ok = ( mDNSu8 ) ( ( count == sizeof( NDIS_PNP_CAPABILITIES ) ) && ( pNPC->Flags & NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE ) );
+
+exit:
+
+ if ( pNPC != NULL )
+ {
+ free( pNPC );
+ }
+
+ if ( handle != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( handle );
+ }
+
+ dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter returns %s\n", ok ? "true" : "false" );
+
+ return ( mDNSu8 ) ok;
+}
+
+
+mDNSlocal void
+SendWakeupPacket( mDNS * const inMDNS, LPSOCKADDR addr, INT addrlen, const char * buf, INT buflen, INT numTries, INT msecSleep )
+{
+ mDNSBool repeat = ( numTries == 1 ) ? mDNStrue : mDNSfalse;
+ SOCKET sock;
+ int num;
+ mStatus err;
+
+ ( void ) inMDNS;
+
+ sock = socket( addr->sa_family, SOCK_DGRAM, IPPROTO_UDP );
+ require_action( sock != INVALID_SOCKET, exit, err = mStatus_UnknownErr );
+
+ while ( numTries-- )
+ {
+ num = sendto( sock, ( const char* ) buf, buflen, 0, addr, addrlen );
+
+ if ( num != buflen )
+ {
+ LogMsg( "SendWakeupPacket error: sent %d bytes: %d\n", num, WSAGetLastError() );
+ }
+
+ if ( repeat )
+ {
+ num = sendto( sock, buf, buflen, 0, addr, addrlen );
+
+ if ( num != buflen )
+ {
+ LogMsg( "SendWakeupPacket error: sent %d bytes: %d\n", num, WSAGetLastError() );
+ }
+ }
+
+ if ( msecSleep )
+ {
+ Sleep( msecSleep );
+ }
+ }
+
+exit:
+
+ if ( sock != INVALID_SOCKET )
+ {
+ closesocket( sock );
+ }
+}
+
+
+mDNSlocal void _cdecl
+SendMulticastWakeupPacket( void *arg )
+{
+ MulticastWakeupStruct *info = ( MulticastWakeupStruct* ) arg;
+
+ if ( info )
+ {
+ SendWakeupPacket( info->inMDNS, ( LPSOCKADDR ) &info->addr, sizeof( info->addr ), ( const char* ) info->data, sizeof( info->data ), info->numTries, info->msecSleep );
+ free( info );
+ }
+
+ _endthread();
+}
+
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ DEBUG_UNUSED( m );
+ DEBUG_UNUSED( rr );
+ DEBUG_UNUSED( result );
+}
diff --git a/src/tools/mdnssd/mDNSWin32.h b/src/tools/mdnssd/mDNSWin32.h
new file mode 100644
index 0000000000..6b5b435664
--- /dev/null
+++ b/src/tools/mdnssd/mDNSWin32.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MDNS_WIN32__
+#define __MDNS_WIN32__
+
+#include "CommonServices.h"
+
+#if( !defined( _WIN32_WCE ) )
+ #include <mswsock.h>
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+#include "uDNS.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+typedef void ( *TCPUserCallback )();
+
+struct TCPSocket_struct
+{
+ TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags
+ SOCKET fd;
+ BOOL connected;
+ TCPUserCallback userCallback;
+ void * userContext;
+ BOOL closed;
+ mDNS * m;
+};
+
+
+struct UDPSocket_struct
+{
+ mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+ mDNSAddr addr; // This is initialized by our code. If we don't get the
+ // dstAddr from WSARecvMsg we use this value instead.
+ SOCKET fd;
+ LPFN_WSARECVMSG recvMsgPtr;
+ DNSMessage packet;
+ struct mDNSInterfaceData *ifd;
+ UDPSocket *next;
+ mDNS *m;
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct mDNSInterfaceData
+
+ @abstract Structure containing interface-specific data.
+*/
+
+typedef struct mDNSInterfaceData mDNSInterfaceData;
+struct mDNSInterfaceData
+{
+ char name[ 128 ];
+ uint32_t index;
+ uint32_t scopeID;
+ struct UDPSocket_struct sock;
+ NetworkInterfaceInfo interfaceInfo;
+ mDNSBool hostRegistered;
+ mDNSInterfaceData * next;
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef ReportStatusFunc
+*/
+typedef void (*ReportStatusFunc)(int inType, const char *inFormat, ...);
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct mDNS_PlatformSupport_struct
+
+ @abstract Structure containing platform-specific data.
+*/
+
+struct mDNS_PlatformSupport_struct
+{
+ HANDLE mainThread;
+ HANDLE checkFileSharesTimer;
+ mDNSs32 checkFileSharesTimeout;
+ ReportStatusFunc reportStatusFunc;
+ time_t nextDHCPLeaseExpires;
+ char nbname[ 32 ];
+ char nbdomain[ 32 ];
+ mDNSBool smbFileSharing;
+ mDNSBool smbPrintSharing;
+ ServiceRecordSet smbSRS;
+ AuthRecord smbSubTypes[ 2 ];
+ mDNSBool registeredLoopback4;
+ int interfaceCount;
+ mDNSInterfaceData * interfaceList;
+ mDNSInterfaceData * inactiveInterfaceList;
+ struct UDPSocket_struct unicastSock4;
+ struct UDPSocket_struct unicastSock6;
+ DWORD osMajorVersion;
+ DWORD osMinorVersion;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct ifaddrs
+
+ @abstract Interface information
+*/
+
+struct ifaddrs
+{
+ struct ifaddrs * ifa_next;
+ char * ifa_name;
+ u_int ifa_flags;
+ struct sockaddr * ifa_addr;
+ struct sockaddr * ifa_netmask;
+ struct sockaddr * ifa_broadaddr;
+ struct sockaddr * ifa_dstaddr;
+ BYTE ifa_physaddr[6];
+ BOOL ifa_dhcpEnabled;
+ time_t ifa_dhcpLeaseExpires;
+ mDNSu8 ifa_womp;
+ void * ifa_data;
+
+ struct
+ {
+ uint32_t index;
+
+ } ifa_extra;
+};
+
+
+extern void InterfaceListDidChange( mDNS * const inMDNS );
+extern void ComputerDescriptionDidChange( mDNS * const inMDNS );
+extern void TCPIPConfigDidChange( mDNS * const inMDNS );
+extern void DynDNSConfigDidChange( mDNS * const inMDNS );
+extern void FileSharingDidChange( mDNS * const inMDNS );
+extern void FirewallDidChange( mDNS * const inMDNS );
+extern mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock );
+extern mStatus SetupInterfaceList( mDNS * const inMDNS );
+extern mStatus TearDownInterfaceList( mDNS * const inMDNS );
+extern BOOL IsWOMPEnabled();
+extern void DispatchSocketEvents( mDNS * const inMDNS );
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __MDNS_WIN32__
diff --git a/src/tools/mdnssd/main.c b/src/tools/mdnssd/main.c
new file mode 100644
index 0000000000..e83d01dff5
--- /dev/null
+++ b/src/tools/mdnssd/main.c
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+#include "Shellapi.h"
+
+//===========================================================================================================================
+// main
+//===========================================================================================================================
+int APIENTRY wWinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPTSTR lpCmdLine,
+ int nCmdShow)
+{
+ LPWSTR *argv;
+ int argc, res;
+
+ argv = CommandLineToArgvW(lpCmdLine, &argc);
+ if (argv == NULL)
+ argc = 0;
+ res = Main( argc, argv );
+ LocalFree(argv);
+ return res;
+}
diff --git a/src/tools/mdnssd/mdnssd.pro b/src/tools/mdnssd/mdnssd.pro
index 2272a5bf61..cd1fb378c0 100644
--- a/src/tools/mdnssd/mdnssd.pro
+++ b/src/tools/mdnssd/mdnssd.pro
@@ -1,19 +1,12 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2011-10-14T10:22:27
-#
-#-------------------------------------------------
-
QT -= gui
-QT += core
-
+TEST = 0
+include(../../../qtcreator.pri)
+CONFIG -= console testlib TEST
TARGET = mdnssd
-CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
-include(../../../qtcreator.pri)
DESTDIR = $$IDE_BIN_PATH
DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/mdnsd\\\" MDNS_DEBUGMSGS=0
@@ -21,10 +14,6 @@ DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/
SOURCES += \
uds_daemon.c \
uDNS.c \
- PosixDaemon.c \
- PlatformCommon.c \
- mDNSUNP.c \
- mDNSPosix.c \
mDNSDebug.c \
mDNS.c \
GenLinkedList.c \
@@ -35,9 +24,7 @@ SOURCES += \
HEADERS += \
uds_daemon.h \
uDNS.h \
- PlatformCommon.h \
mDNSUNP.h \
- mDNSPosix.h \
mDNSEmbeddedAPI.h \
mDNSDebug.h \
GenLinkedList.h \
@@ -46,14 +33,70 @@ HEADERS += \
DebugServices.h \
dns_sd.h
+linux-* {
+SOURCES += mDNSPosix.c \
+ PlatformCommon.c \
+ PosixDaemon.c \
+ mDNSUNP.c
+
+HEADERS +=\
+ PlatformCommon.h \
+ mDNSPosix.h
+}
+
*-g++ {
QMAKE_CFLAGS += -Wno-unused-but-set-variable
QMAKE_CXXFLAGS += -Wno-unused-but-set-variable
}
+
linux-* {
DEFINES += _GNU_SOURCE HAVE_IPV6 NOT_HAVE_SA_LEN USES_NETLINK HAVE_LINUX TARGET_OS_LINUX
}
+
macx {
DEFINES += HAVE_IPV6 __MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 __APPLE_USE_RFC_2292
}
+win32 {
+ HEADERS += \
+ CommonServices.h \
+ DebugServices.h \
+ Firewall.h \
+ mDNSWin32.h \
+ Poll.h \
+ resource.h \
+ Secret.h \
+ Service.h \
+ RegNames.h
+
+ SOURCES += \
+ DebugServices.c \
+ Firewall.cpp \
+ LegacyNATTraversal.c \
+ main.c \
+ mDNSWin32.c \
+ Poll.c \
+ Secret.c \
+ Service.c
+
+ RC_FILE = Service.rc
+
+ MC_FILES += \
+ EventLog.mc
+
+ OTHER_FILES += \
+ $$MC_FILES \
+ Service.rc
+
+ DEFINES += HAVE_IPV6 _WIN32_WINNT=0x0501 NDEBUG MDNS_DEBUGMSGS=0 TARGET_OS_WIN32 WIN32_LEAN_AND_MEAN USE_TCP_LOOPBACK PLATFORM_NO_STRSEP PLATFORM_NO_EPIPE PLATFORM_NO_RLIMIT UNICODE _UNICODE _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 _LEGACY_NAT_TRAVERSAL_
+ LIBS += ws2_32.lib advapi32.lib ole32.lib oleaut32.lib iphlpapi.lib netapi32.lib user32.lib powrprof.lib shell32.lib
+
+ mc.output = ${QMAKE_FILE_BASE}.h
+ mc.commands = mc ${QMAKE_FILE_NAME}
+ mc.input = MC_FILES
+ mc.CONFIG += no_link target_predeps explicit_dependencies
+ QMAKE_EXTRA_COMPILERS += mc
+}
+
+target.path=/bin
+INSTALLS+=target
diff --git a/src/tools/mdnssd/resource.h b/src/tools/mdnssd/resource.h
new file mode 100644
index 0000000000..d968af909d
--- /dev/null
+++ b/src/tools/mdnssd/resource.h
@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Service.rc
+//
+
+#define IDS_SERVICE_DESCRIPTION 100
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/tools/mdnssd/uDNS.c b/src/tools/mdnssd/uDNS.c
index 57cfc1a689..a36338c906 100755
--- a/src/tools/mdnssd/uDNS.c
+++ b/src/tools/mdnssd/uDNS.c
@@ -101,7 +101,7 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random)
#pragma mark - Name Server List Management
#endif
-mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf)
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf)
{
DNSServer **p = &m->DNSServers;
DNSServer *tmp = mDNSNULL;
@@ -114,16 +114,16 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
if (!d) d = (const domainname *)"";
- LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface, scoped);
+ LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface1, scoped);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
while (*p) // Check if we already have this {interface,address,port,domain} tuple registered
{
- if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled &&
+ if ((*p)->scoped == scoped && (*p)->interface1 == interface1 && (*p)->teststate != DNSServer_Disabled &&
mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d))
{
- if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface);
+ if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface1);
(*p)->flags &= ~DNSServer_FlagDelete;
tmp = *p;
*p = tmp->next;
@@ -143,7 +143,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
{
NumUnicastDNSServers++;
(*p)->scoped = scoped;
- (*p)->interface = interface;
+ (*p)->interface1 = interface1;
(*p)->addr = *addr;
(*p)->port = port;
(*p)->flags = DNSServer_FlagNew;
@@ -4215,7 +4215,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time
- else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
+ else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface1, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
}
}
diff --git a/src/tools/mdnssd/uds_daemon.c b/src/tools/mdnssd/uds_daemon.c
index c962173498..d2c2a24efb 100644
--- a/src/tools/mdnssd/uds_daemon.c
+++ b/src/tools/mdnssd/uds_daemon.c
@@ -1,13 +1,13 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-#if defined(_WIN32)
+#if defined(WIN32)
+#include <WinSock2.h>
#include <process.h>
#define usleep(X) Sleep(((X)+999)/1000)
#else
@@ -76,166 +77,166 @@ int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid
#endif
typedef enum
- {
- t_uninitialized,
- t_morecoming,
- t_complete,
- t_error,
- t_terminated
- } transfer_state;
+ {
+ t_uninitialized,
+ t_morecoming,
+ t_complete,
+ t_error,
+ t_terminated
+ } transfer_state;
typedef struct request_state request_state;
typedef void (*req_termination_fn)(request_state *request);
typedef struct registered_record_entry
- {
- struct registered_record_entry *next;
- mDNSu32 key;
- client_context_t regrec_client_context;
- request_state *request;
- mDNSBool external_advertise;
- mDNSInterfaceID origInterfaceID;
- AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?)
- } registered_record_entry;
+ {
+ struct registered_record_entry *next;
+ mDNSu32 key;
+ client_context_t regrec_client_context;
+ request_state *request;
+ mDNSBool external_advertise;
+ mDNSInterfaceID origInterfaceID;
+ AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?)
+ } registered_record_entry;
// A single registered service: ServiceRecordSet + bookkeeping
// Note that we duplicate some fields from parent service_info object
// to facilitate cleanup, when instances and parent may be deallocated at different times.
typedef struct service_instance
- {
- struct service_instance *next;
- request_state *request;
- AuthRecord *subtypes;
- mDNSBool renameonmemfree; // Set on config change when we deregister original name
+ {
+ struct service_instance *next;
+ request_state *request;
+ AuthRecord *subtypes;
+ mDNSBool renameonmemfree; // Set on config change when we deregister original name
mDNSBool clientnotified; // Has client been notified of successful registration yet?
- mDNSBool default_local; // is this the "local." from an empty-string registration?
- mDNSBool external_advertise; // is this is being advertised externally?
- domainname domain;
- ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct
- } service_instance;
+ mDNSBool default_local; // is this the "local." from an empty-string registration?
+ mDNSBool external_advertise; // is this is being advertised externally?
+ domainname domain;
+ ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct
+ } service_instance;
// for multi-domain default browsing
typedef struct browser_t
- {
- struct browser_t *next;
- domainname domain;
- DNSQuestion q;
- } browser_t;
+ {
+ struct browser_t *next;
+ domainname domain;
+ DNSQuestion q;
+ } browser_t;
struct request_state
- {
- request_state *next;
- request_state *primary; // If this operation is on a shared socket, pointer to primary
- // request_state for the original DNSServiceCreateConnection() operation
- dnssd_sock_t sd;
- dnssd_sock_t errsd;
- mDNSu32 uid;
- void * platform_data;
-
- // Note: On a shared connection these fields in the primary structure, including hdr, are re-used
- // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the
- // operation is, we don't know if we're going to need to allocate a new request_state or not.
- transfer_state ts;
- mDNSu32 hdr_bytes; // bytes of header already read
- ipc_msg_hdr hdr;
- mDNSu32 data_bytes; // bytes of message data already read
- char *msgbuf; // pointer to data storage to pass to free()
- const char *msgptr; // pointer to data to be read from (may be modified)
- char *msgend; // pointer to byte after last byte of message
-
- // reply, termination, error, and client context info
- int no_reply; // don't send asynchronous replies to client
- mDNSs32 time_blocked; // record time of a blocked client
- int unresponsiveness_reports;
- struct reply_state *replies; // corresponding (active) reply list
- req_termination_fn terminate;
- DNSServiceFlags flags;
-
- union
- {
- registered_record_entry *reg_recs; // list of registrations for a connection-oriented request
- struct
- {
- mDNSInterfaceID interface_id;
- mDNSBool default_domain;
- mDNSBool ForceMCast;
- domainname regtype;
- browser_t *browsers;
- } browser;
- struct
- {
- mDNSInterfaceID InterfaceID;
- mDNSu16 txtlen;
- void *txtdata;
- mDNSIPPort port;
- domainlabel name;
- char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
- domainname type;
- mDNSBool default_domain;
- domainname host;
- mDNSBool autoname; // Set if this name is tied to the Computer Name
- mDNSBool autorename; // Set if this client wants us to automatically rename on conflict
- mDNSBool allowremotequery; // Respond to unicast queries from outside the local link?
- int num_subtypes;
- service_instance *instances;
- } servicereg;
- struct
- {
- mDNSInterfaceID interface_id;
- mDNSu32 flags;
- mDNSu32 protocol;
- DNSQuestion q4;
- DNSQuestion *q42;
- DNSQuestion q6;
- DNSQuestion *q62;
- } addrinfo;
- struct
- {
- mDNSIPPort ReqExt; // External port we originally requested, for logging purposes
- NATTraversalInfo NATinfo;
- } pm;
- struct
- {
+ {
+ request_state *next;
+ request_state *primary; // If this operation is on a shared socket, pointer to primary
+ // request_state for the original DNSServiceCreateConnection() operation
+ dnssd_sock_t sd;
+ dnssd_sock_t errsd;
+ mDNSu32 uid;
+ void * platform_data;
+
+ // Note: On a shared connection these fields in the primary structure, including hdr, are re-used
+ // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the
+ // operation is, we don't know if we're going to need to allocate a new request_state or not.
+ transfer_state ts;
+ mDNSu32 hdr_bytes; // bytes of header already read
+ ipc_msg_hdr hdr;
+ mDNSu32 data_bytes; // bytes of message data already read
+ char *msgbuf; // pointer to data storage to pass to free()
+ const char *msgptr; // pointer to data to be read from (may be modified)
+ char *msgend; // pointer to byte after last byte of message
+
+ // reply, termination, error, and client context info
+ int no_reply; // don't send asynchronous replies to client
+ mDNSs32 time_blocked; // record time of a blocked client
+ int unresponsiveness_reports;
+ struct reply_state *replies; // corresponding (active) reply list
+ req_termination_fn terminate;
+ DNSServiceFlags flags;
+
+ union
+ {
+ registered_record_entry *reg_recs; // list of registrations for a connection-oriented request
+ struct
+ {
+ mDNSInterfaceID interface_id;
+ mDNSBool default_domain;
+ mDNSBool ForceMCast;
+ domainname regtype;
+ browser_t *browsers;
+ } browser;
+ struct
+ {
+ mDNSInterfaceID InterfaceID;
+ mDNSu16 txtlen;
+ void *txtdata;
+ mDNSIPPort port;
+ domainlabel name;
+ char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+ domainname type;
+ mDNSBool default_domain;
+ domainname host;
+ mDNSBool autoname; // Set if this name is tied to the Computer Name
+ mDNSBool autorename; // Set if this client wants us to automatically rename on conflict
+ mDNSBool allowremotequery; // Respond to unicast queries from outside the local link?
+ int num_subtypes;
+ service_instance *instances;
+ } servicereg;
+ struct
+ {
+ mDNSInterfaceID interface_id;
+ mDNSu32 flags;
+ mDNSu32 protocol;
+ DNSQuestion q4;
+ DNSQuestion *q42;
+ DNSQuestion q6;
+ DNSQuestion *q62;
+ } addrinfo;
+ struct
+ {
+ mDNSIPPort ReqExt; // External port we originally requested, for logging purposes
+ NATTraversalInfo NATinfo;
+ } pm;
+ struct
+ {
#if 0
- DNSServiceFlags flags;
+ DNSServiceFlags flags;
#endif
- DNSQuestion q_all;
- DNSQuestion q_default;
- } enumeration;
- struct
- {
- DNSQuestion q;
- DNSQuestion *q2;
- } queryrecord;
- struct
- {
- DNSQuestion qtxt;
- DNSQuestion qsrv;
- const ResourceRecord *txt;
- const ResourceRecord *srv;
- mDNSs32 ReportTime;
- mDNSBool external_advertise;
- } resolve;
- } u;
- };
+ DNSQuestion q_all;
+ DNSQuestion q_default;
+ } enumeration;
+ struct
+ {
+ DNSQuestion q;
+ DNSQuestion *q2;
+ } queryrecord;
+ struct
+ {
+ DNSQuestion qtxt;
+ DNSQuestion qsrv;
+ const ResourceRecord *txt;
+ const ResourceRecord *srv;
+ mDNSs32 ReportTime;
+ mDNSBool external_advertise;
+ } resolve;
+ } u;
+ };
// struct physically sits between ipc message header and call-specific fields in the message buffer
typedef struct
- {
- DNSServiceFlags flags; // Note: This field is in NETWORK byte order
- mDNSu32 ifi; // Note: This field is in NETWORK byte order
- DNSServiceErrorType error; // Note: This field is in NETWORK byte order
- } reply_hdr;
+ {
+ DNSServiceFlags flags; // Note: This field is in NETWORK byte order
+ mDNSu32 ifi; // Note: This field is in NETWORK byte order
+ DNSServiceErrorType error; // Note: This field is in NETWORK byte order
+ } reply_hdr;
typedef struct reply_state
- {
- struct reply_state *next; // If there are multiple unsent replies
- mDNSu32 totallen;
- mDNSu32 nwriten;
- ipc_msg_hdr mhdr[1];
- reply_hdr rhdr[1];
- } reply_state;
+ {
+ struct reply_state *next; // If there are multiple unsent replies
+ mDNSu32 totallen;
+ mDNSu32 nwriten;
+ ipc_msg_hdr mhdr[1];
+ reply_hdr rhdr[1];
+ } reply_state;
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -266,7 +267,7 @@ static ARListElem *LocalDomainEnumRecords; // List of locally-generated PTR
mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-only PTR records plus records we get from the network
#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
- // n get_string() calls w/o buffer overrun
+ // n get_string() calls w/o buffer overrun
// initialization, setup/teardown functions
// If a platform specifies its own PID file name, we use that
@@ -281,317 +282,318 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-o
#endif
mDNSlocal void FatalError(char *errmsg)
- {
- LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno));
- *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
- abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
- }
+ {
+ char* ptr = NULL;
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno));
+ *ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
+ abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
+ }
mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l)
- {
- mDNSu32 ret;
- char *data = (char*) &ret;
- put_uint32(l, &data);
- return ret;
- }
+ {
+ mDNSu32 ret;
+ char *data = (char*) &ret;
+ put_uint32(l, &data);
+ return ret;
+ }
// hack to search-replace perror's to LogMsg's
mDNSlocal void my_perror(char *errmsg)
- {
- LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
- }
+ {
+ LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
+ }
mDNSlocal void abort_request(request_state *req)
- {
- if (req->terminate == (req_termination_fn)~0)
- { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; }
-
- // First stop whatever mDNSCore operation we were doing
- // If this is actually a shared connection operation, then its req->terminate function will scan
- // the all_requests list and terminate any subbordinate operations sharing this file descriptor
- if (req->terminate) req->terminate(req);
-
- if (!dnssd_SocketValid(req->sd))
- { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; }
-
- // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies
- if (!req->primary)
- {
- if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd);
- else LogOperation("%3d: Removing FD", req->sd);
- udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us
- if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; }
-
- while (req->replies) // free pending replies
- {
- reply_state *ptr = req->replies;
- req->replies = req->replies->next;
- freeL("reply_state (abort)", ptr);
- }
- }
-
- // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure
+ {
+ if (req->terminate == (req_termination_fn)~0)
+ { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; }
+
+ // First stop whatever mDNSCore operation we were doing
+ // If this is actually a shared connection operation, then its req->terminate function will scan
+ // the all_requests list and terminate any subbordinate operations sharing this file descriptor
+ if (req->terminate) req->terminate(req);
+
+ if (!dnssd_SocketValid(req->sd))
+ { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; }
+
+ // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies
+ if (!req->primary)
+ {
+ if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd);
+ else LogOperation("%3d: Removing FD", req->sd);
+ udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us
+ if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; }
+
+ while (req->replies) // free pending replies
+ {
+ reply_state *ptr = req->replies;
+ req->replies = req->replies->next;
+ freeL("reply_state (abort)", ptr);
+ }
+ }
+
+ // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure
#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
- // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses
- // for detecting when the memory for an object is inadvertently freed while the object is still on some list
- req->sd = req->errsd = -2;
+ // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses
+ // for detecting when the memory for an object is inadvertently freed while the object is still on some list
+ req->sd = req->errsd = -2;
#else
- req->sd = req->errsd = dnssd_InvalidSocket;
+ req->sd = req->errsd = dnssd_InvalidSocket;
#endif
- // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request
- req->terminate = (req_termination_fn)~0;
- }
+ // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request
+ req->terminate = (req_termination_fn)~0;
+ }
mDNSlocal void AbortUnlinkAndFree(request_state *req)
- {
- request_state **p = &all_requests;
- abort_request(req);
- while (*p && *p != req) p=&(*p)->next;
- if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); }
- else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req);
- }
+ {
+ request_state **p = &all_requests;
+ abort_request(req);
+ while (*p && *p != req) p=&(*p)->next;
+ if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); }
+ else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req);
+ }
mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, request_state *const request)
- {
- reply_state *reply;
-
- if ((unsigned)datalen < sizeof(reply_hdr))
- {
- LogMsg("ERROR: create_reply - data length less than length of required fields");
- return NULL;
- }
-
- reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr));
- if (!reply) FatalError("ERROR: malloc");
-
- reply->next = mDNSNULL;
- reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr);
- reply->nwriten = 0;
-
- reply->mhdr->version = VERSION;
- reply->mhdr->datalen = (mDNSu32)datalen;
- reply->mhdr->ipc_flags = 0;
- reply->mhdr->op = op;
- reply->mhdr->client_context = request->hdr.client_context;
- reply->mhdr->reg_index = 0;
-
- return reply;
- }
+ {
+ reply_state *reply;
+
+ if ((unsigned)datalen < sizeof(reply_hdr))
+ {
+ LogMsg("ERROR: create_reply - data length less than length of required fields");
+ return NULL;
+ }
+
+ reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr));
+ if (!reply) FatalError("ERROR: malloc");
+
+ reply->next = mDNSNULL;
+ reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr);
+ reply->nwriten = 0;
+
+ reply->mhdr->version = VERSION;
+ reply->mhdr->datalen = (mDNSu32)datalen;
+ reply->mhdr->ipc_flags = 0;
+ reply->mhdr->op = op;
+ reply->mhdr->client_context = request->hdr.client_context;
+ reply->mhdr->reg_index = 0;
+
+ return reply;
+ }
// Append a reply to the list in a request object
// If our request is sharing a connection, then we append our reply_state onto the primary's list
mDNSlocal void append_reply(request_state *req, reply_state *rep)
- {
- request_state *r = req->primary ? req->primary : req;
- reply_state **ptr = &r->replies;
- while (*ptr) ptr = &(*ptr)->next;
- *ptr = rep;
- rep->next = NULL;
- }
+ {
+ request_state *r = req->primary ? req->primary : req;
+ reply_state **ptr = &r->replies;
+ while (*ptr) ptr = &(*ptr)->next;
+ *ptr = rep;
+ rep->next = NULL;
+ }
// Generates a response message giving name, type, domain, plus interface index,
// suitable for a browse result or service registration result.
// On successful completion rep is set to point to a malloc'd reply_state struct
mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const mDNSInterfaceID id,
- request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
- {
- domainlabel name;
- domainname type, dom;
- *rep = NULL;
- if (!DeconstructServiceName(servicename, &name, &type, &dom))
- return kDNSServiceErr_Invalid;
- else
- {
- char namestr[MAX_DOMAIN_LABEL+1];
- char typestr[MAX_ESCAPED_DOMAIN_NAME];
- char domstr [MAX_ESCAPED_DOMAIN_NAME];
- int len;
- char *data;
-
- ConvertDomainLabelToCString_unescaped(&name, namestr);
- ConvertDomainNameToCString(&type, typestr);
- ConvertDomainNameToCString(&dom, domstr);
-
- // Calculate reply data length
- len = sizeof(DNSServiceFlags);
- len += sizeof(mDNSu32); // if index
- len += sizeof(DNSServiceErrorType);
- len += (int) (strlen(namestr) + 1);
- len += (int) (strlen(typestr) + 1);
- len += (int) (strlen(domstr) + 1);
-
- // Build reply header
- *rep = create_reply(op, len, request);
- (*rep)->rhdr->flags = dnssd_htonl(flags);
- (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
- (*rep)->rhdr->error = dnssd_htonl(err);
-
- // Build reply body
- data = (char *)&(*rep)->rhdr[1];
- put_string(namestr, &data);
- put_string(typestr, &data);
- put_string(domstr, &data);
-
- return mStatus_NoError;
- }
- }
+ request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+ {
+ domainlabel name;
+ domainname type, dom;
+ *rep = NULL;
+ if (!DeconstructServiceName(servicename, &name, &type, &dom))
+ return kDNSServiceErr_Invalid;
+ else
+ {
+ char namestr[MAX_DOMAIN_LABEL+1];
+ char typestr[MAX_ESCAPED_DOMAIN_NAME];
+ char domstr [MAX_ESCAPED_DOMAIN_NAME];
+ int len;
+ char *data;
+
+ ConvertDomainLabelToCString_unescaped(&name, namestr);
+ ConvertDomainNameToCString(&type, typestr);
+ ConvertDomainNameToCString(&dom, domstr);
+
+ // Calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += (int) (strlen(namestr) + 1);
+ len += (int) (strlen(typestr) + 1);
+ len += (int) (strlen(domstr) + 1);
+
+ // Build reply header
+ *rep = create_reply(op, len, request);
+ (*rep)->rhdr->flags = dnssd_htonl(flags);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+ (*rep)->rhdr->error = dnssd_htonl(err);
+
+ // Build reply body
+ data = (char *)&(*rep)->rhdr[1];
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+
+ return mStatus_NoError;
+ }
+ }
// Special support to enable the DNSServiceBrowse call made by Bonjour Browser
// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id,
- request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
- {
- char namestr[MAX_DOMAIN_LABEL+1];
- char typestr[MAX_ESCAPED_DOMAIN_NAME];
- static const char domstr[] = ".";
- int len;
- char *data;
-
- *rep = NULL;
-
- // 1. Put first label in namestr
- ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr);
-
- // 2. Put second label and "local" into typestr
- mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename));
-
- // Calculate reply data length
- len = sizeof(DNSServiceFlags);
- len += sizeof(mDNSu32); // if index
- len += sizeof(DNSServiceErrorType);
- len += (int) (strlen(namestr) + 1);
- len += (int) (strlen(typestr) + 1);
- len += (int) (strlen(domstr) + 1);
-
- // Build reply header
- *rep = create_reply(op, len, request);
- (*rep)->rhdr->flags = dnssd_htonl(flags);
- (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
- (*rep)->rhdr->error = dnssd_htonl(err);
-
- // Build reply body
- data = (char *)&(*rep)->rhdr[1];
- put_string(namestr, &data);
- put_string(typestr, &data);
- put_string(domstr, &data);
- }
+ request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+ {
+ char namestr[MAX_DOMAIN_LABEL+1];
+ char typestr[MAX_ESCAPED_DOMAIN_NAME];
+ static const char domstr[] = ".";
+ int len;
+ char *data;
+
+ *rep = NULL;
+
+ // 1. Put first label in namestr
+ ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr);
+
+ // 2. Put second label and "local" into typestr
+ mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename));
+
+ // Calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += (int) (strlen(namestr) + 1);
+ len += (int) (strlen(typestr) + 1);
+ len += (int) (strlen(domstr) + 1);
+
+ // Build reply header
+ *rep = create_reply(op, len, request);
+ (*rep)->rhdr->flags = dnssd_htonl(flags);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+ (*rep)->rhdr->error = dnssd_htonl(err);
+
+ // Build reply body
+ data = (char *)&(*rep)->rhdr[1];
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+ }
// Returns a resource record (allocated w/ malloc) containing the data found in an IPC message
// Data must be in the following format: flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional) ttl
// (ttl only extracted/set if ttl argument is non-zero). Returns NULL for a bad-parameter error
mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, int validate_flags)
- {
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- char name[256];
- int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name));
- mDNSu16 type = get_uint16(&request->msgptr, request->msgend);
- mDNSu16 class = get_uint16(&request->msgptr, request->msgend);
- mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
- const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
- mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
- int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
- AuthRecord *rr;
- mDNSInterfaceID InterfaceID;
- AuthRecType artype;
-
- request->flags = flags;
-
- if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
-
- if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; }
-
- if (validate_flags &&
- !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
- !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
- {
- LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
- return NULL;
- }
-
- rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
- if (!rr) FatalError("ERROR: malloc");
-
- InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (InterfaceID == mDNSInterface_LocalOnly)
- artype = AuthRecordLocalOnly;
- else if (InterfaceID == mDNSInterface_P2P)
- artype = AuthRecordP2P;
- else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
- artype = AuthRecordAnyIncludeP2P;
- else
- artype = AuthRecordAny;
-
- mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
- (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
-
- if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
- {
- LogMsg("ERROR: bad name: %s", name);
- freeL("AuthRecord/read_rr_from_ipc_msg", rr);
- return NULL;
- }
-
- if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
- rr->resrec.rrclass = class;
- rr->resrec.rdlength = rdlen;
- rr->resrec.rdata->MaxRDLength = rdlen;
- mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen);
- if (GetTTL) rr->resrec.rroriginalttl = ttl;
- rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
- SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
- return rr;
- }
+ {
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ char name[256];
+ int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name));
+ mDNSu16 type = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 class = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
+ int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ AuthRecord *rr;
+ mDNSInterfaceID InterfaceID;
+ AuthRecType artype;
+
+ request->flags = flags;
+
+ if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
+
+ if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; }
+
+ if (validate_flags &&
+ !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
+ !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
+ {
+ LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
+ return NULL;
+ }
+
+ rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+ if (!rr) FatalError("ERROR: malloc");
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
+ artype = AuthRecordAnyIncludeP2P;
+ else
+ artype = AuthRecordAny;
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
+ (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
+
+ if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
+ {
+ LogMsg("ERROR: bad name: %s", name);
+ freeL("AuthRecord/read_rr_from_ipc_msg", rr);
+ return NULL;
+ }
+
+ if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
+ rr->resrec.rrclass = class;
+ rr->resrec.rdlength = rdlen;
+ rr->resrec.rdata->MaxRDLength = rdlen;
+ mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen);
+ if (GetTTL) rr->resrec.rroriginalttl = ttl;
+ rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+ SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
+ return rr;
+ }
mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
- {
- domainlabel n;
- domainname d, t;
+ {
+ domainlabel n;
+ domainname d, t;
- if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
- if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
- if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
- if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
- return 0;
- }
+ if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
+ if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
+ return 0;
+ }
mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len)
- {
- int n = send(s, ptr, len, 0);
- // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us
- // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)).
- // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong.
- if (n < len)
- LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)",
- s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno));
- }
+ {
+ int n = send(s, ptr, len, 0);
+ // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us
+ // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)).
+ // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong.
+ if (n < len)
+ LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)",
+ s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno));
+ }
#if 0
mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const domainname * const d, const DNameListElem * const doms)
{
- const DNameListElem *delem = mDNSNULL;
- int bestDelta = -1; // the delta of the best match, lower is better
- int dLabels = 0;
- mDNSBool allow = mDNSfalse;
-
- if (SystemUID(request->uid)) return mDNStrue;
-
- dLabels = CountLabels(d);
- for (delem = doms; delem; delem = delem->next)
- {
- if (delem->uid)
- {
- int delemLabels = CountLabels(&delem->name);
- int delta = dLabels - delemLabels;
- if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta)))
- {
- bestDelta = delta;
- allow = (allow || (delem->uid == request->uid));
- }
- }
- }
-
- return bestDelta == -1 ? mDNStrue : allow;
+ const DNameListElem *delem = mDNSNULL;
+ int bestDelta = -1; // the delta of the best match, lower is better
+ int dLabels = 0;
+ mDNSBool allow = mDNSfalse;
+
+ if (SystemUID(request->uid)) return mDNStrue;
+
+ dLabels = CountLabels(d);
+ for (delem = doms; delem; delem = delem->next)
+ {
+ if (delem->uid)
+ {
+ int delemLabels = CountLabels(&delem->name);
+ int delta = dLabels - delemLabels;
+ if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta)))
+ {
+ bestDelta = delta;
+ allow = (allow || (delem->uid == request->uid));
+ }
+ }
+ }
+
+ return bestDelta == -1 ? mDNStrue : allow;
}
#endif
@@ -602,60 +604,60 @@ mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const d
#endif
mDNSlocal void external_start_advertising_helper(service_instance *const instance)
- {
- AuthRecord *st = instance->subtypes;
- ExtraResourceRecord *e;
- int i;
-
- if (mDNSIPPortIsZero(instance->request->u.servicereg.port))
- {
- LogInfo("external_start_advertising_helper: Not registering service with port number zero");
- return;
- }
+ {
+ AuthRecord *st = instance->subtypes;
+ ExtraResourceRecord *e;
+ int i;
+
+ if (mDNSIPPortIsZero(instance->request->u.servicereg.port))
+ {
+ LogInfo("external_start_advertising_helper: Not registering service with port number zero");
+ return;
+ }
#if APPLE_OSX_mDNSResponder
- // Update packet filter if p2p interface already exists, otherwise,
- // if will be updated when we get the KEV_DL_IF_ATTACHED event for
- // the interface. Called here since we don't call external_start_advertising_service()
- // with the SRV record when advertising a service.
- mDNSInitPacketFilter();
+ // Update packet filter if p2p interface already exists, otherwise,
+ // if will be updated when we get the KEV_DL_IF_ATTACHED event for
+ // the interface. Called here since we don't call external_start_advertising_service()
+ // with the SRV record when advertising a service.
+ mDNSInitPacketFilter();
#endif // APPLE_OSX_mDNSResponder
- if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
-
- for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
- external_start_advertising_service(&st[i].resrec);
-
- external_start_advertising_service(&instance->srs.RR_PTR.resrec);
- external_start_advertising_service(&instance->srs.RR_TXT.resrec);
-
- for (e = instance->srs.Extras; e; e = e->next)
- external_start_advertising_service(&e->r.resrec);
-
- instance->external_advertise = mDNStrue;
- }
+ if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
+
+ for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+ external_start_advertising_service(&st[i].resrec);
+
+ external_start_advertising_service(&instance->srs.RR_PTR.resrec);
+ external_start_advertising_service(&instance->srs.RR_TXT.resrec);
+
+ for (e = instance->srs.Extras; e; e = e->next)
+ external_start_advertising_service(&e->r.resrec);
+
+ instance->external_advertise = mDNStrue;
+ }
mDNSlocal void external_stop_advertising_helper(service_instance *const instance)
- {
- AuthRecord *st = instance->subtypes;
- ExtraResourceRecord *e;
- int i;
-
- if (!instance->external_advertise) return;
-
- LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
-
- for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
- external_stop_advertising_service(&st[i].resrec);
-
- external_stop_advertising_service(&instance->srs.RR_PTR.resrec);
- external_stop_advertising_service(&instance->srs.RR_TXT.resrec);
-
- for (e = instance->srs.Extras; e; e = e->next)
- external_stop_advertising_service(&e->r.resrec);
-
- instance->external_advertise = mDNSfalse;
- }
+ {
+ AuthRecord *st = instance->subtypes;
+ ExtraResourceRecord *e;
+ int i;
+
+ if (!instance->external_advertise) return;
+
+ LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
+
+ for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+ external_stop_advertising_service(&st[i].resrec);
+
+ external_stop_advertising_service(&instance->srs.RR_PTR.resrec);
+ external_stop_advertising_service(&instance->srs.RR_TXT.resrec);
+
+ for (e = instance->srs.Extras; e; e = e->next)
+ external_stop_advertising_service(&e->r.resrec);
+
+ instance->external_advertise = mDNSfalse;
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -664,1062 +666,1062 @@ mDNSlocal void external_stop_advertising_helper(service_instance *const instance
#endif
mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
- {
- ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
- (void)m; // Unused
+ {
+ ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
+ (void)m; // Unused
- if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
+ if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
- LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec));
+ LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec));
- if (rr->resrec.rdata != &rr->rdatastorage)
- freeL("Extra RData", rr->resrec.rdata);
- freeL("ExtraResourceRecord/FreeExtraRR", extra);
- }
+ if (rr->resrec.rdata != &rr->rdatastorage)
+ freeL("Extra RData", rr->resrec.rdata);
+ freeL("ExtraResourceRecord/FreeExtraRR", extra);
+ }
mDNSlocal void unlink_and_free_service_instance(service_instance *srv)
- {
- ExtraResourceRecord *e = srv->srs.Extras, *tmp;
-
- external_stop_advertising_helper(srv);
-
- // clear pointers from parent struct
- if (srv->request)
- {
- service_instance **p = &srv->request->u.servicereg.instances;
- while (*p)
- {
- if (*p == srv) { *p = (*p)->next; break; }
- p = &(*p)->next;
- }
- }
-
- while (e)
- {
- e->r.RecordContext = e;
- tmp = e;
- e = e->next;
- FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
- }
-
- if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
- freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
-
- if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; }
- freeL("service_instance", srv);
- }
+ {
+ ExtraResourceRecord *e = srv->srs.Extras, *tmp;
+
+ external_stop_advertising_helper(srv);
+
+ // clear pointers from parent struct
+ if (srv->request)
+ {
+ service_instance **p = &srv->request->u.servicereg.instances;
+ while (*p)
+ {
+ if (*p == srv) { *p = (*p)->next; break; }
+ p = &(*p)->next;
+ }
+ }
+
+ while (e)
+ {
+ e->r.RecordContext = e;
+ tmp = e;
+ e = e->next;
+ FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
+ }
+
+ if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
+ freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
+
+ if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; }
+ freeL("service_instance", srv);
+ }
// Count how many other service records we have locally with the same name, but different rdata.
// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
- {
- int count = 0;
- ResourceRecord *r = &srs->RR_SRV.resrec;
- AuthRecord *rr;
+ {
+ int count = 0;
+ ResourceRecord *r = &srs->RR_SRV.resrec;
+ AuthRecord *rr;
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r))
- count++;
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r))
+ count++;
- verbosedebugf("%d peer registrations for %##s", count, r->name->c);
- return(count);
- }
+ verbosedebugf("%d peer registrations for %##s", count, r->name->c);
+ return(count);
+ }
mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
- {
- int count = 0;
- AuthRecord *rr;
- for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
- if (rr->resrec.rrtype == kDNSType_SRV &&
- mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) &&
- SameDomainName(rr->resrec.name, srv))
- count++;
- return(count);
- }
+ {
+ int count = 0;
+ AuthRecord *rr;
+ for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV &&
+ mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) &&
+ SameDomainName(rr->resrec.name, srv))
+ count++;
+ return(count);
+ }
mDNSlocal void SendServiceRemovalNotification(ServiceRecordSet *const srs)
- {
- reply_state *rep;
- service_instance *instance = srs->ServiceContext;
- if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError)
- LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
- else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; }
- }
+ {
+ reply_state *rep;
+ service_instance *instance = srs->ServiceContext;
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError)
+ LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; }
+ }
// service registration callback performs three duties - frees memory for deregistered services,
// handles name conflicts, and delivers completed registration information to the client
mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
- {
- mStatus err;
- mDNSBool SuppressError = mDNSfalse;
- service_instance *instance;
- reply_state *rep;
- (void)m; // Unused
-
- if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
-
- instance = srs->ServiceContext;
- if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
-
- // don't send errors up to client for wide-area, empty-string registrations
- if (instance->request &&
- instance->request->u.servicereg.default_domain &&
- !instance->default_local)
- SuppressError = mDNStrue;
-
- if (mDNS_LoggingEnabled)
- {
- const char *const fmt =
- (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" :
- (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" :
- (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" :
- "%s DNSServiceRegister(%##s, %u) %s %d";
- char prefix[16] = "---:";
- if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd);
- LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port),
- SuppressError ? "suppressed error" : "CALLBACK", result);
- }
-
- if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
-
- if (result == mStatus_NoError)
- {
- if (instance->request->u.servicereg.allowremotequery)
- {
- ExtraResourceRecord *e;
- srs->RR_ADV.AllowRemoteQuery = mDNStrue;
- srs->RR_PTR.AllowRemoteQuery = mDNStrue;
- srs->RR_SRV.AllowRemoteQuery = mDNStrue;
- srs->RR_TXT.AllowRemoteQuery = mDNStrue;
- for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
- }
-
- if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
- LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
- else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
-
- if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))
- {
- LogInfo("regservice_callback: calling external_start_advertising_helper()");
- external_start_advertising_helper(instance);
- }
- if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
- RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
- }
- else if (result == mStatus_MemFree)
- {
- if (instance->request && instance->renameonmemfree)
- {
- external_stop_advertising_helper(instance);
- instance->renameonmemfree = 0;
- err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name);
- if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
- // error should never happen - safest to log and continue
- }
- else
- unlink_and_free_service_instance(instance);
- }
- else if (result == mStatus_NameConflict)
- {
- if (instance->request->u.servicereg.autorename)
- {
- external_stop_advertising_helper(instance);
- if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
- {
- // On conflict for an autoname service, rename and reregister *all* autoname services
- IncrementLabelSuffix(&m->nicelabel, mDNStrue);
- mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange()
- }
- else // On conflict for a non-autoname service, rename and reregister just that one service
- {
- if (instance->clientnotified) SendServiceRemovalNotification(srs);
- mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
- }
- }
- else
- {
- if (!SuppressError)
- {
- if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
- LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
- else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
- }
- unlink_and_free_service_instance(instance);
- }
- }
- else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict
- {
- if (!SuppressError)
- {
- if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
- LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
- else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
- }
- }
- }
+ {
+ mStatus err;
+ mDNSBool SuppressError = mDNSfalse;
+ service_instance *instance;
+ reply_state *rep;
+ (void)m; // Unused
+
+ if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
+
+ instance = srs->ServiceContext;
+ if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
+
+ // don't send errors up to client for wide-area, empty-string registrations
+ if (instance->request &&
+ instance->request->u.servicereg.default_domain &&
+ !instance->default_local)
+ SuppressError = mDNStrue;
+
+ if (mDNS_LoggingEnabled)
+ {
+ const char *const fmt =
+ (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" :
+ (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" :
+ (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" :
+ "%s DNSServiceRegister(%##s, %u) %s %d";
+ char prefix[16] = "---:";
+ if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd);
+ LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port),
+ SuppressError ? "suppressed error" : "CALLBACK", result);
+ }
+
+ if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
+
+ if (result == mStatus_NoError)
+ {
+ if (instance->request->u.servicereg.allowremotequery)
+ {
+ ExtraResourceRecord *e;
+ srs->RR_ADV.AllowRemoteQuery = mDNStrue;
+ srs->RR_PTR.AllowRemoteQuery = mDNStrue;
+ srs->RR_SRV.AllowRemoteQuery = mDNStrue;
+ srs->RR_TXT.AllowRemoteQuery = mDNStrue;
+ for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
+ }
+
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+
+ if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("regservice_callback: calling external_start_advertising_helper()");
+ external_start_advertising_helper(instance);
+ }
+ if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+ RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
+ }
+ else if (result == mStatus_MemFree)
+ {
+ if (instance->request && instance->renameonmemfree)
+ {
+ external_stop_advertising_helper(instance);
+ instance->renameonmemfree = 0;
+ err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+ // error should never happen - safest to log and continue
+ }
+ else
+ unlink_and_free_service_instance(instance);
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (instance->request->u.servicereg.autorename)
+ {
+ external_stop_advertising_helper(instance);
+ if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+ {
+ // On conflict for an autoname service, rename and reregister *all* autoname services
+ IncrementLabelSuffix(&m->nicelabel, mDNStrue);
+ mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange()
+ }
+ else // On conflict for a non-autoname service, rename and reregister just that one service
+ {
+ if (instance->clientnotified) SendServiceRemovalNotification(srs);
+ mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
+ }
+ }
+ else
+ {
+ if (!SuppressError)
+ {
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+ }
+ unlink_and_free_service_instance(instance);
+ }
+ }
+ else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict
+ {
+ if (!SuppressError)
+ {
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+ }
+ }
+ }
mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
- {
- (void)m; // Unused
- if (!rr->RecordContext) // parent struct already freed by termination callback
- {
- if (result == mStatus_NoError)
- LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr));
- else
- {
- if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
-
- // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
- // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
- // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
- // to free the latest rdata for which the update_callback was never called with.
- if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
- freeL("AuthRecord/regrecord_callback", rr);
- }
- }
- else
- {
- registered_record_entry *re = rr->RecordContext;
- request_state *request = re->request;
-
- if (mDNS_LoggingEnabled)
- {
- char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" :
- (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" :
- (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" :
- "%3d: DNSServiceRegisterRecord(%u %s) %d";
- LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result);
- }
-
- if (result != mStatus_MemFree)
- {
- int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType);
- reply_state *reply = create_reply(reg_record_reply_op, len, request);
- reply->mhdr->client_context = re->regrec_client_context;
- reply->rhdr->flags = dnssd_htonl(0);
- reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
- reply->rhdr->error = dnssd_htonl(result);
- append_reply(request, reply);
- }
-
- if (result)
- {
- // unlink from list, free memory
- registered_record_entry **ptr = &request->u.reg_recs;
- while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
- if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
- *ptr = (*ptr)->next;
- freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
- freeL("registered_record_entry regrecord_callback", re);
- }
- else
- {
- if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
-
- if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P)))
- {
- LogInfo("regrecord_callback: calling external_start_advertising_service");
- external_start_advertising_service(&rr->resrec);
- re->external_advertise = mDNStrue;
- }
- }
- }
- }
+ {
+ (void)m; // Unused
+ if (!rr->RecordContext) // parent struct already freed by termination callback
+ {
+ if (result == mStatus_NoError)
+ LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr));
+ else
+ {
+ if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+
+ // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
+ // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
+ // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
+ // to free the latest rdata for which the update_callback was never called with.
+ if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
+ freeL("AuthRecord/regrecord_callback", rr);
+ }
+ }
+ else
+ {
+ registered_record_entry *re = rr->RecordContext;
+ request_state *request = re->request;
+
+ if (mDNS_LoggingEnabled)
+ {
+ char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" :
+ (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" :
+ (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" :
+ "%3d: DNSServiceRegisterRecord(%u %s) %d";
+ LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result);
+ }
+
+ if (result != mStatus_MemFree)
+ {
+ int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType);
+ reply_state *reply = create_reply(reg_record_reply_op, len, request);
+ reply->mhdr->client_context = re->regrec_client_context;
+ reply->rhdr->flags = dnssd_htonl(0);
+ reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
+ reply->rhdr->error = dnssd_htonl(result);
+ append_reply(request, reply);
+ }
+
+ if (result)
+ {
+ // unlink from list, free memory
+ registered_record_entry **ptr = &request->u.reg_recs;
+ while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+ *ptr = (*ptr)->next;
+ freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
+ freeL("registered_record_entry regrecord_callback", re);
+ }
+ else
+ {
+ if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
+
+ if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("regrecord_callback: calling external_start_advertising_service");
+ external_start_advertising_service(&rr->resrec);
+ re->external_advertise = mDNStrue;
+ }
+ }
+ }
+ }
mDNSlocal void connection_termination(request_state *request)
- {
- // When terminating a shared connection, we need to scan the all_requests list
- // and terminate any subbordinate operations sharing this file descriptor
- request_state **req = &all_requests;
-
- LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd);
-
- while (*req)
- {
- if ((*req)->primary == request)
- {
- // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
- request_state *tmp = *req;
- if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd);
- if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd);
- abort_request(tmp);
- *req = tmp->next;
- freeL("request_state/connection_termination", tmp);
- }
- else
- req = &(*req)->next;
- }
-
- while (request->u.reg_recs)
- {
- registered_record_entry *ptr = request->u.reg_recs;
- LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec));
- request->u.reg_recs = request->u.reg_recs->next;
- ptr->rr->RecordContext = NULL;
- if (ptr->external_advertise)
- {
- ptr->external_advertise = mDNSfalse;
- external_stop_advertising_service(&ptr->rr->resrec);
- }
- mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us
- freeL("registered_record_entry/connection_termination", ptr);
- }
- }
+ {
+ // When terminating a shared connection, we need to scan the all_requests list
+ // and terminate any subbordinate operations sharing this file descriptor
+ request_state **req = &all_requests;
+
+ LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd);
+
+ while (*req)
+ {
+ if ((*req)->primary == request)
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ request_state *tmp = *req;
+ if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd);
+ if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd);
+ abort_request(tmp);
+ *req = tmp->next;
+ freeL("request_state/connection_termination", tmp);
+ }
+ else
+ req = &(*req)->next;
+ }
+
+ while (request->u.reg_recs)
+ {
+ registered_record_entry *ptr = request->u.reg_recs;
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec));
+ request->u.reg_recs = request->u.reg_recs->next;
+ ptr->rr->RecordContext = NULL;
+ if (ptr->external_advertise)
+ {
+ ptr->external_advertise = mDNSfalse;
+ external_stop_advertising_service(&ptr->rr->resrec);
+ }
+ mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us
+ freeL("registered_record_entry/connection_termination", ptr);
+ }
+ }
mDNSlocal void handle_cancel_request(request_state *request)
- {
- request_state **req = &all_requests;
- LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]);
- while (*req)
- {
- if ((*req)->primary == request &&
- (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
- (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1])
- {
- // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
- request_state *tmp = *req;
- abort_request(tmp);
- *req = tmp->next;
- freeL("request_state/handle_cancel_request", tmp);
- }
- else
- req = &(*req)->next;
- }
- }
+ {
+ request_state **req = &all_requests;
+ LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]);
+ while (*req)
+ {
+ if ((*req)->primary == request &&
+ (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+ (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1])
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ request_state *tmp = *req;
+ abort_request(tmp);
+ *req = tmp->next;
+ freeL("request_state/handle_cancel_request", tmp);
+ }
+ else
+ req = &(*req)->next;
+ }
+ }
mDNSlocal mStatus handle_regrecord_request(request_state *request)
- {
- mStatus err = mStatus_BadParamErr;
- AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
- if (rr)
- {
- registered_record_entry *re;
- // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
- // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
- if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
- rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
- rr->resrec.rrtype == kDNSType_CNAME))
- {
- freeL("AuthRecord/handle_regrecord_request", rr);
- return (mStatus_BadParamErr);
- }
- // allocate registration entry, link into list
- re = mallocL("registered_record_entry", sizeof(registered_record_entry));
- if (!re) FatalError("ERROR: malloc");
- re->key = request->hdr.reg_index;
- re->rr = rr;
- re->regrec_client_context = request->hdr.client_context;
- re->request = request;
- re->external_advertise = mDNSfalse;
- rr->RecordContext = re;
- rr->RecordCallback = regrecord_callback;
-
- re->origInterfaceID = rr->resrec.InterfaceID;
- if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;
+ {
+ mStatus err = mStatus_BadParamErr;
+ AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
+ if (rr)
+ {
+ registered_record_entry *re;
+ // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
+ // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
+ rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
+ rr->resrec.rrtype == kDNSType_CNAME))
+ {
+ freeL("AuthRecord/handle_regrecord_request", rr);
+ return (mStatus_BadParamErr);
+ }
+ // allocate registration entry, link into list
+ re = mallocL("registered_record_entry", sizeof(registered_record_entry));
+ if (!re) FatalError("ERROR: malloc");
+ re->key = request->hdr.reg_index;
+ re->rr = rr;
+ re->regrec_client_context = request->hdr.client_context;
+ re->request = request;
+ re->external_advertise = mDNSfalse;
+ rr->RecordContext = re;
+ rr->RecordCallback = regrecord_callback;
+
+ re->origInterfaceID = rr->resrec.InterfaceID;
+ if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;
#if 0
- if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError);
+ if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError);
#endif
- if (rr->resrec.rroriginalttl == 0)
- rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
-
- LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec));
- err = mDNS_Register(&mDNSStorage, rr);
- if (err)
- {
- LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
- freeL("registered_record_entry", re);
- freeL("registered_record_entry/AuthRecord", rr);
- }
- else
- {
- re->next = request->u.reg_recs;
- request->u.reg_recs = re;
- }
- }
- return(err);
- }
+ if (rr->resrec.rroriginalttl == 0)
+ rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
+
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec));
+ err = mDNS_Register(&mDNSStorage, rr);
+ if (err)
+ {
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
+ freeL("registered_record_entry", re);
+ freeL("registered_record_entry/AuthRecord", rr);
+ }
+ else
+ {
+ re->next = request->u.reg_recs;
+ request->u.reg_recs = re;
+ }
+ }
+ return(err);
+ }
mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m);
mDNSlocal void regservice_termination_callback(request_state *request)
- {
- if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; }
- while (request->u.servicereg.instances)
- {
- service_instance *p = request->u.servicereg.instances;
- request->u.servicereg.instances = request->u.servicereg.instances->next;
- // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
- LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP",
- request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
-
- external_stop_advertising_helper(p);
-
- // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance
- // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing
- // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time
- // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
- // because by then we might have already freed p
- p->request = NULL;
- if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p);
- // Don't touch service_instance *p after this -- it's likely to have been freed already
- }
- if (request->u.servicereg.txtdata)
- { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
- if (request->u.servicereg.autoname)
- {
- // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
- request->u.servicereg.autoname = mDNSfalse;
- UpdateDeviceInfoRecord(&mDNSStorage);
- }
- }
+ {
+ if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; }
+ while (request->u.servicereg.instances)
+ {
+ service_instance *p = request->u.servicereg.instances;
+ request->u.servicereg.instances = request->u.servicereg.instances->next;
+ // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP",
+ request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
+
+ external_stop_advertising_helper(p);
+
+ // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance
+ // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing
+ // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time
+ // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
+ // because by then we might have already freed p
+ p->request = NULL;
+ if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p);
+ // Don't touch service_instance *p after this -- it's likely to have been freed already
+ }
+ if (request->u.servicereg.txtdata)
+ { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+ if (request->u.servicereg.autoname)
+ {
+ // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
+ request->u.servicereg.autoname = mDNSfalse;
+ UpdateDeviceInfoRecord(&mDNSStorage);
+ }
+ }
mDNSlocal request_state *LocateSubordinateRequest(request_state *request)
- {
- request_state *req;
- for (req = all_requests; req; req = req->next)
- if (req->primary == request &&
- req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
- req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req);
- return(request);
- }
+ {
+ request_state *req;
+ for (req = all_requests; req; req = req->next)
+ if (req->primary == request &&
+ req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+ req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req);
+ return(request);
+ }
mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl)
- {
- ServiceRecordSet *srs = &instance->srs;
- mStatus result;
- int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
- ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
- if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
-
- mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
- extra->r.resrec.rrtype = rrtype;
- extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
- extra->r.resrec.rdlength = rdlen;
- mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
-
- result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl,
- (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0);
- if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
-
- extra->ClientID = request->hdr.reg_index;
- if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))))
- {
- LogInfo("add_record_to_service: calling external_start_advertising_service");
- external_start_advertising_service(&extra->r.resrec);
- }
- return result;
- }
+ {
+ ServiceRecordSet *srs = &instance->srs;
+ mStatus result;
+ int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
+ if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
+ extra->r.resrec.rrtype = rrtype;
+ extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
+ extra->r.resrec.rdlength = rdlen;
+ mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+
+ result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl,
+ (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0);
+ if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
+
+ extra->ClientID = request->hdr.reg_index;
+ if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))))
+ {
+ LogInfo("add_record_to_service: calling external_start_advertising_service");
+ external_start_advertising_service(&extra->r.resrec);
+ }
+ return result;
+ }
mDNSlocal mStatus handle_add_request(request_state *request)
- {
- service_instance *i;
- mStatus result = mStatus_UnknownErr;
- DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend);
- mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend);
- mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
- const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
- mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
- if (!ttl) ttl = DefaultTTLforRRType(rrtype);
- (void)flags; // Unused
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
- if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
-
- if (request->terminate != regservice_termination_callback)
- { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
-
- // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
- // in the application. See radar://9165807.
- if (mDNSIPPortIsZero(request->u.servicereg.port))
- { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
-
- LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
- (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
-
- for (i = request->u.servicereg.instances; i; i = i->next)
- {
- result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl);
- if (result && i->default_local) break;
- else result = mStatus_NoError; // suppress non-local default errors
- }
-
- return(result);
- }
+ {
+ service_instance *i;
+ mStatus result = mStatus_UnknownErr;
+ DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend);
+ mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
+ if (!ttl) ttl = DefaultTTLforRRType(rrtype);
+ (void)flags; // Unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+ // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
+ // in the application. See radar://9165807.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+ LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
+
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl);
+ if (result && i->default_local) break;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+ return(result);
+ }
mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen)
- {
- mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse;
- (void)m; // Unused
-
- // There are three cases.
- //
- // 1. We have updated the primary TXT record of the service
- // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord
- // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord
- //
- // external_advertise is set if we have advertised at least once during the initial addition
- // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain
- // checks during the first time and hence we don't do any checks here
- if (external_advertise)
- {
- ResourceRecord ext = rr->resrec;
- if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
- SetNewRData(&ext, oldrd, oldrdlen);
- external_stop_advertising_service(&ext);
- LogInfo("update_callback: calling external_start_advertising_service");
- external_start_advertising_service(&rr->resrec);
- }
+ {
+ mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse;
+ (void)m; // Unused
+
+ // There are three cases.
+ //
+ // 1. We have updated the primary TXT record of the service
+ // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord
+ // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord
+ //
+ // external_advertise is set if we have advertised at least once during the initial addition
+ // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain
+ // checks during the first time and hence we don't do any checks here
+ if (external_advertise)
+ {
+ ResourceRecord ext = rr->resrec;
+ if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
+ SetNewRData(&ext, oldrd, oldrdlen);
+ external_stop_advertising_service(&ext);
+ LogInfo("update_callback: calling external_start_advertising_service");
+ external_start_advertising_service(&rr->resrec);
+ }
exit:
- if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd);
- }
+ if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd);
+ }
mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise)
- {
- mStatus result;
- const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
- RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize);
- if (!newrd) FatalError("ERROR: malloc");
- newrd->MaxRDLength = (mDNSu16) rdsize;
- mDNSPlatformMemCopy(&newrd->u, rdata, rdlen);
-
- // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
- // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
- // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
- if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
-
- if (external_advertise) rr->UpdateContext = (void *)external_advertise;
-
- result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
- if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); }
- return result;
- }
+ {
+ mStatus result;
+ const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize);
+ if (!newrd) FatalError("ERROR: malloc");
+ newrd->MaxRDLength = (mDNSu16) rdsize;
+ mDNSPlatformMemCopy(&newrd->u, rdata, rdlen);
+
+ // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+ // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+ // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+ if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
+
+ if (external_advertise) rr->UpdateContext = (void *)external_advertise;
+
+ result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
+ if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); }
+ return result;
+ }
mDNSlocal mStatus handle_update_request(request_state *request)
- {
- const ipc_msg_hdr *const hdr = &request->hdr;
- mStatus result = mStatus_BadReferenceErr;
- service_instance *i;
- AuthRecord *rr = NULL;
-
- // get the message data
- DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused
- mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
- const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
- mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
- (void)flags; // Unused
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
- if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
-
- if (request->terminate == connection_termination)
- {
- // update an individually registered record
- registered_record_entry *reptr;
- for (reptr = request->u.reg_recs; reptr; reptr = reptr->next)
- {
- if (reptr->key == hdr->reg_index)
- {
- result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise);
- LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)",
- request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>");
- goto end;
- }
- }
- result = mStatus_BadReferenceErr;
- goto end;
- }
-
- if (request->terminate != regservice_termination_callback)
- { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
-
- // For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
- if (mDNSIPPortIsZero(request->u.servicereg.port))
- { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
-
- // update the saved off TXT data for the service
- if (hdr->reg_index == TXT_RECORD_INDEX)
- {
- if (request->u.servicereg.txtdata)
- { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
- if (rdlen > 0)
- {
- request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen);
- if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc");
- mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen);
- }
- request->u.servicereg.txtlen = rdlen;
- }
-
- // update a record from a service record set
- for (i = request->u.servicereg.instances; i; i = i->next)
- {
- if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
- else
- {
- ExtraResourceRecord *e;
- for (e = i->srs.Extras; e; e = e->next)
- if (e->ClientID == hdr->reg_index) { rr = &e->r; break; }
- }
-
- if (!rr) { result = mStatus_BadReferenceErr; goto end; }
- result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise);
- if (result && i->default_local) goto end;
- else result = mStatus_NoError; // suppress non-local default errors
- }
+ {
+ const ipc_msg_hdr *const hdr = &request->hdr;
+ mStatus result = mStatus_BadReferenceErr;
+ service_instance *i;
+ AuthRecord *rr = NULL;
+
+ // get the message data
+ DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate == connection_termination)
+ {
+ // update an individually registered record
+ registered_record_entry *reptr;
+ for (reptr = request->u.reg_recs; reptr; reptr = reptr->next)
+ {
+ if (reptr->key == hdr->reg_index)
+ {
+ result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise);
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)",
+ request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>");
+ goto end;
+ }
+ }
+ result = mStatus_BadReferenceErr;
+ goto end;
+ }
+
+ if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+ // For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+ // update the saved off TXT data for the service
+ if (hdr->reg_index == TXT_RECORD_INDEX)
+ {
+ if (request->u.servicereg.txtdata)
+ { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+ if (rdlen > 0)
+ {
+ request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen);
+ if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc");
+ mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen);
+ }
+ request->u.servicereg.txtlen = rdlen;
+ }
+
+ // update a record from a service record set
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
+ else
+ {
+ ExtraResourceRecord *e;
+ for (e = i->srs.Extras; e; e = e->next)
+ if (e->ClientID == hdr->reg_index) { rr = &e->r; break; }
+ }
+
+ if (!rr) { result = mStatus_BadReferenceErr; goto end; }
+ result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise);
+ if (result && i->default_local) goto end;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
end:
- if (request->terminate == regservice_termination_callback)
- LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd,
- (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
- rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
+ if (request->terminate == regservice_termination_callback)
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+ rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
- return(result);
- }
+ return(result);
+ }
// remove a resource record registered via DNSServiceRegisterRecord()
mDNSlocal mStatus remove_record(request_state *request)
- {
- mStatus err = mStatus_UnknownErr;
- registered_record_entry *e, **ptr = &request->u.reg_recs;
-
- while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next;
- if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; }
- e = *ptr;
- *ptr = e->next; // unlink
-
- LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec));
- e->rr->RecordContext = NULL;
- if (e->external_advertise)
- {
- external_stop_advertising_service(&e->rr->resrec);
- e->external_advertise = mDNSfalse;
- }
- err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e
- if (err)
- {
- LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
- freeL("registered_record_entry AuthRecord remove_record", e->rr);
- }
-
- freeL("registered_record_entry remove_record", e);
- return err;
- }
+ {
+ mStatus err = mStatus_UnknownErr;
+ registered_record_entry *e, **ptr = &request->u.reg_recs;
+
+ while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; }
+ e = *ptr;
+ *ptr = e->next; // unlink
+
+ LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec));
+ e->rr->RecordContext = NULL;
+ if (e->external_advertise)
+ {
+ external_stop_advertising_service(&e->rr->resrec);
+ e->external_advertise = mDNSfalse;
+ }
+ err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e
+ if (err)
+ {
+ LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
+ freeL("registered_record_entry AuthRecord remove_record", e->rr);
+ }
+
+ freeL("registered_record_entry remove_record", e);
+ return err;
+ }
mDNSlocal mStatus remove_extra(const request_state *const request, service_instance *const serv, mDNSu16 *const rrtype)
- {
- mStatus err = mStatus_BadReferenceErr;
- ExtraResourceRecord *ptr;
-
- for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
- {
- if (ptr->ClientID == request->hdr.reg_index) // found match
- {
- *rrtype = ptr->r.resrec.rrtype;
- if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec);
- err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr);
- break;
- }
- }
- return err;
- }
+ {
+ mStatus err = mStatus_BadReferenceErr;
+ ExtraResourceRecord *ptr;
+
+ for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
+ {
+ if (ptr->ClientID == request->hdr.reg_index) // found match
+ {
+ *rrtype = ptr->r.resrec.rrtype;
+ if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec);
+ err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr);
+ break;
+ }
+ }
+ return err;
+ }
mDNSlocal mStatus handle_removerecord_request(request_state *request)
- {
- mStatus err = mStatus_BadReferenceErr;
- get_flags(&request->msgptr, request->msgend); // flags unused
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
- if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
-
- if (request->terminate == connection_termination)
- err = remove_record(request); // remove individually registered record
- else if (request->terminate != regservice_termination_callback)
- { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
- else
- {
- service_instance *i;
- mDNSu16 rrtype = 0;
- LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd,
- (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
- rrtype ? DNSTypeName(rrtype) : "<NONE>");
- for (i = request->u.servicereg.instances; i; i = i->next)
- {
- err = remove_extra(request, i, &rrtype);
- if (err && i->default_local) break;
- else err = mStatus_NoError; // suppress non-local default errors
- }
- }
-
- return(err);
- }
+ {
+ mStatus err = mStatus_BadReferenceErr;
+ get_flags(&request->msgptr, request->msgend); // flags unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate == connection_termination)
+ err = remove_record(request); // remove individually registered record
+ else if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+ else
+ {
+ service_instance *i;
+ mDNSu16 rrtype = 0;
+ LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+ rrtype ? DNSTypeName(rrtype) : "<NONE>");
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ err = remove_extra(request, i, &rrtype);
+ if (err && i->default_local) break;
+ else err = mStatus_NoError; // suppress non-local default errors
+ }
+ }
+
+ return(err);
+ }
// If there's a comma followed by another character,
// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
// Otherwise, it returns a pointer to the final nul at the end of the string
mDNSlocal char *FindFirstSubType(char *p)
- {
- while (*p)
- {
- if (p[0] == '\\' && p[1]) p += 2;
- else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
- else p++;
- }
- return(p);
- }
+ {
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1]) p += 2;
+ else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
+ else p++;
+ }
+ return(p);
+ }
// If there's a comma followed by another character,
// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
// Otherwise, it returns a pointer to the final nul at the end of the string
mDNSlocal char *FindNextSubType(char *p)
- {
- while (*p)
- {
- if (p[0] == '\\' && p[1]) // If escape character
- p += 2; // ignore following character
- else if (p[0] == ',') // If we found a comma
- {
- if (p[1]) *p++ = 0;
- return(p);
- }
- else if (p[0] == '.')
- return(mDNSNULL);
- else p++;
- }
- return(p);
- }
+ {
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1]) // If escape character
+ p += 2; // ignore following character
+ else if (p[0] == ',') // If we found a comma
+ {
+ if (p[1]) *p++ = 0;
+ return(p);
+ }
+ else if (p[0] == '.')
+ return(mDNSNULL);
+ else p++;
+ }
+ return(p);
+ }
// Returns -1 if illegal subtype found
mDNSexport mDNSs32 ChopSubTypes(char *regtype)
- {
- mDNSs32 NumSubTypes = 0;
- char *stp = FindFirstSubType(regtype);
- while (stp && *stp) // If we found a comma...
- {
- if (*stp == ',') return(-1);
- NumSubTypes++;
- stp = FindNextSubType(stp);
- }
- if (!stp) return(-1);
- return(NumSubTypes);
- }
+ {
+ mDNSs32 NumSubTypes = 0;
+ char *stp = FindFirstSubType(regtype);
+ while (stp && *stp) // If we found a comma...
+ {
+ if (*stp == ',') return(-1);
+ NumSubTypes++;
+ stp = FindNextSubType(stp);
+ }
+ if (!stp) return(-1);
+ return(NumSubTypes);
+ }
mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
- {
- AuthRecord *st = mDNSNULL;
- if (NumSubTypes)
- {
- mDNSs32 i;
- st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
- if (!st) return(mDNSNULL);
- for (i = 0; i < NumSubTypes; i++)
- {
- mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
- while (*p) p++;
- p++;
- if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
- { freeL("ServiceSubTypes", st); return(mDNSNULL); }
- }
- }
- return(st);
- }
+ {
+ AuthRecord *st = mDNSNULL;
+ if (NumSubTypes)
+ {
+ mDNSs32 i;
+ st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
+ if (!st) return(mDNSNULL);
+ for (i = 0; i < NumSubTypes; i++)
+ {
+ mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
+ while (*p) p++;
+ p++;
+ if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
+ { freeL("ServiceSubTypes", st); return(mDNSNULL); }
+ }
+ }
+ return(st);
+ }
mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain)
- {
- service_instance **ptr, *instance;
- const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0;
- const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
- mStatus result;
- mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
- mDNSu32 regFlags = 0;
-
- if (interfaceID == mDNSInterface_P2P)
- {
- interfaceID = mDNSInterface_Any;
- regFlags |= regFlagIncludeP2P;
- }
- else if (request->flags & kDNSServiceFlagsIncludeP2P)
- regFlags |= regFlagIncludeP2P;
-
- // client guarantees that record names are unique
- if (request->flags & kDNSServiceFlagsForce)
- regFlags |= regFlagKnownUnique;
-
- // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
- // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
- // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
- // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local")
- // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.)
- if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any;
-
- for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next)
- {
- if (SameDomainName(&(*ptr)->domain, domain))
- {
- LogMsg("register_service_instance: domain %##s already registered for %#s.%##s",
- domain->c, &request->u.servicereg.name, &request->u.servicereg.type);
- return mStatus_AlreadyRegistered;
- }
- }
-
- if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6)
- {
- // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains,
- // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not.
- // <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6
- if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp"))
- {
- DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain);
- if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported);
- }
- }
-
- instance = mallocL("service_instance", sizeof(*instance) + extra_size);
- if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
-
- instance->next = mDNSNULL;
- instance->request = request;
- instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string);
- instance->renameonmemfree = 0;
- instance->clientnotified = mDNSfalse;
- instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal);
- instance->external_advertise = mDNSfalse;
- AssignDomainName(&instance->domain, domain);
-
- if (request->u.servicereg.num_subtypes && !instance->subtypes)
- { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
-
- result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
- &request->u.servicereg.name, &request->u.servicereg.type, domain,
- request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL,
- request->u.servicereg.port,
- request->u.servicereg.txtdata, request->u.servicereg.txtlen,
- instance->subtypes, request->u.servicereg.num_subtypes,
- interfaceID, regservice_callback, instance, regFlags);
-
- if (!result)
- {
- *ptr = instance; // Append this to the end of our request->u.servicereg.instances list
- LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED",
- instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
- }
- else
- {
- LogMsg("register_service_instance %#s.%##s%##s error %d",
- &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result);
- unlink_and_free_service_instance(instance);
- }
-
- return result;
- }
+ {
+ service_instance **ptr, *instance;
+ const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0;
+ const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
+ mStatus result;
+ mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
+ mDNSu32 regFlags = 0;
+
+ if (interfaceID == mDNSInterface_P2P)
+ {
+ interfaceID = mDNSInterface_Any;
+ regFlags |= regFlagIncludeP2P;
+ }
+ else if (request->flags & kDNSServiceFlagsIncludeP2P)
+ regFlags |= regFlagIncludeP2P;
+
+ // client guarantees that record names are unique
+ if (request->flags & kDNSServiceFlagsForce)
+ regFlags |= regFlagKnownUnique;
+
+ // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
+ // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
+ // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
+ // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local")
+ // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.)
+ if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any;
+
+ for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next)
+ {
+ if (SameDomainName(&(*ptr)->domain, domain))
+ {
+ LogMsg("register_service_instance: domain %##s already registered for %#s.%##s",
+ domain->c, &request->u.servicereg.name, &request->u.servicereg.type);
+ return mStatus_AlreadyRegistered;
+ }
+ }
+
+ if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6)
+ {
+ // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains,
+ // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not.
+ // <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6
+ if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp"))
+ {
+ DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain);
+ if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported);
+ }
+ }
+
+ instance = mallocL("service_instance", sizeof(*instance) + extra_size);
+ if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ instance->next = mDNSNULL;
+ instance->request = request;
+ instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string);
+ instance->renameonmemfree = 0;
+ instance->clientnotified = mDNSfalse;
+ instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal);
+ instance->external_advertise = mDNSfalse;
+ AssignDomainName(&instance->domain, domain);
+
+ if (request->u.servicereg.num_subtypes && !instance->subtypes)
+ { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
+
+ result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
+ &request->u.servicereg.name, &request->u.servicereg.type, domain,
+ request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL,
+ request->u.servicereg.port,
+ request->u.servicereg.txtdata, request->u.servicereg.txtlen,
+ instance->subtypes, request->u.servicereg.num_subtypes,
+ interfaceID, regservice_callback, instance, regFlags);
+
+ if (!result)
+ {
+ *ptr = instance; // Append this to the end of our request->u.servicereg.instances list
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED",
+ instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
+ }
+ else
+ {
+ LogMsg("register_service_instance %#s.%##s%##s error %d",
+ &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result);
+ unlink_and_free_service_instance(instance);
+ }
+
+ return result;
+ }
mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d, const mDNSBool add)
- {
- request_state *request;
+ {
+ request_state *request;
#if APPLE_OSX_mDNSResponder
- machserver_automatic_registration_domain_changed(&d->name, add);
+ machserver_automatic_registration_domain_changed(&d->name, add);
#endif // APPLE_OSX_mDNSResponder
- LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c);
- for (request = all_requests; request; request = request->next)
- {
- if (request->terminate != regservice_termination_callback) continue;
- if (!request->u.servicereg.default_domain) continue;
- if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
- {
- service_instance **ptr = &request->u.servicereg.instances;
- while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
- if (add)
- {
- // If we don't already have this domain in our list for this registration, add it now
- if (!*ptr) register_service_instance(request, &d->name);
- else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name);
- }
- else
- {
- // Normally we should not fail to find the specified instance
- // One case where this can happen is if a uDNS update fails for some reason,
- // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance.
- if (!*ptr)
- LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s",
- &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string);
- else
- {
- DNameListElem *p;
- for (p = AutoRegistrationDomains; p; p=p->next)
- if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
- if (SameDomainName(&d->name, &p->name)) break;
- if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name);
- else
- {
- mStatus err;
- service_instance *si = *ptr;
- *ptr = si->next;
- if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer
- // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer.
- // Otherwise what can happen is this: While our mDNS_DeregisterService is in the
- // process of completing asynchronously, the client cancels the entire operation, so
- // regservice_termination_callback then runs through the whole list deregistering each
- // instance, clearing the backpointers, and then disposing the parent request_state object.
- // However, because this service_instance isn't in the list any more, regservice_termination_callback
- // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally
- // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with
- // a service_instance with a stale si->request backpointer pointing to memory that's already been freed.
- si->request = NULL;
- err = mDNS_DeregisterService(&mDNSStorage, &si->srs);
- if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); }
- }
- }
- }
- }
- }
- }
+ LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c);
+ for (request = all_requests; request; request = request->next)
+ {
+ if (request->terminate != regservice_termination_callback) continue;
+ if (!request->u.servicereg.default_domain) continue;
+ if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+ {
+ service_instance **ptr = &request->u.servicereg.instances;
+ while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+ if (add)
+ {
+ // If we don't already have this domain in our list for this registration, add it now
+ if (!*ptr) register_service_instance(request, &d->name);
+ else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name);
+ }
+ else
+ {
+ // Normally we should not fail to find the specified instance
+ // One case where this can happen is if a uDNS update fails for some reason,
+ // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance.
+ if (!*ptr)
+ LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s",
+ &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string);
+ else
+ {
+ DNameListElem *p;
+ for (p = AutoRegistrationDomains; p; p=p->next)
+ if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+ if (SameDomainName(&d->name, &p->name)) break;
+ if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name);
+ else
+ {
+ mStatus err;
+ service_instance *si = *ptr;
+ *ptr = si->next;
+ if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer
+ // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer.
+ // Otherwise what can happen is this: While our mDNS_DeregisterService is in the
+ // process of completing asynchronously, the client cancels the entire operation, so
+ // regservice_termination_callback then runs through the whole list deregistering each
+ // instance, clearing the backpointers, and then disposing the parent request_state object.
+ // However, because this service_instance isn't in the list any more, regservice_termination_callback
+ // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally
+ // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with
+ // a service_instance with a stale si->request backpointer pointing to memory that's already been freed.
+ si->request = NULL;
+ err = mDNS_DeregisterService(&mDNSStorage, &si->srs);
+ if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); }
+ }
+ }
+ }
+ }
+ }
+ }
mDNSlocal mStatus handle_regservice_request(request_state *request)
- {
- char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
- char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
- char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
- domainname d, srv;
- mStatus err;
-
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID)
- { LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
-
- if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 ||
- get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
- { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
-
- request->flags = flags;
- request->u.servicereg.InterfaceID = InterfaceID;
- request->u.servicereg.instances = NULL;
- request->u.servicereg.txtlen = 0;
- request->u.servicereg.txtdata = NULL;
- mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string);
-
- if (request->msgptr + 2 > request->msgend) request->msgptr = NULL;
- else
- {
- request->u.servicereg.port.b[0] = *request->msgptr++;
- request->u.servicereg.port.b[1] = *request->msgptr++;
- }
-
- request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend);
- if (request->u.servicereg.txtlen)
- {
- request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen);
- if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc");
- mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen);
- }
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- // Check for sub-types after the service type
- request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes
- if (request->u.servicereg.num_subtypes < 0)
- { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
-
- // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
- if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
- { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
-
- if (!name[0])
- {
- request->u.servicereg.name = mDNSStorage.nicelabel;
- request->u.servicereg.autoname = mDNStrue;
- }
- else
- {
- // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
- if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
- {
- int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
- name[newlen] = 0;
- }
- if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name))
- { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); }
- request->u.servicereg.autoname = mDNSfalse;
- }
-
- if (*domain)
- {
- request->u.servicereg.default_domain = mDNSfalse;
- if (!MakeDomainNameFromDNSNameString(&d, domain))
- { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); }
- }
- else
- {
- request->u.servicereg.default_domain = mDNStrue;
- MakeDomainNameFromDNSNameString(&d, "local.");
- }
-
- if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
- {
+ {
+ char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
+ char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
+ char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+ domainname d, srv;
+ mStatus err;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+ if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 ||
+ get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+ request->flags = flags;
+ request->u.servicereg.InterfaceID = InterfaceID;
+ request->u.servicereg.instances = NULL;
+ request->u.servicereg.txtlen = 0;
+ request->u.servicereg.txtdata = NULL;
+ mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string);
+
+ if (request->msgptr + 2 > request->msgend) request->msgptr = NULL;
+ else
+ {
+ request->u.servicereg.port.b[0] = *request->msgptr++;
+ request->u.servicereg.port.b[1] = *request->msgptr++;
+ }
+
+ request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend);
+ if (request->u.servicereg.txtlen)
+ {
+ request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen);
+ if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc");
+ mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen);
+ }
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // Check for sub-types after the service type
+ request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes
+ if (request->u.servicereg.num_subtypes < 0)
+ { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+
+ // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
+ if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
+ { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+
+ if (!name[0])
+ {
+ request->u.servicereg.name = mDNSStorage.nicelabel;
+ request->u.servicereg.autoname = mDNStrue;
+ }
+ else
+ {
+ // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
+ if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
+ {
+ int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
+ name[newlen] = 0;
+ }
+ if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name))
+ { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); }
+ request->u.servicereg.autoname = mDNSfalse;
+ }
+
+ if (*domain)
+ {
+ request->u.servicereg.default_domain = mDNSfalse;
+ if (!MakeDomainNameFromDNSNameString(&d, domain))
+ { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); }
+ }
+ else
+ {
+ request->u.servicereg.default_domain = mDNStrue;
+ MakeDomainNameFromDNSNameString(&d, "local.");
+ }
+
+ if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
+ {
LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, '%#s' '%##s' '%##s'",
- request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr);
- }
-
- if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host))
- { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); }
- request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
- request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
-
- // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
- // a port number of zero. When two instances of the protected client are allowed to run on one
- // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
- if (!mDNSIPPortIsZero(request->u.servicereg.port))
- {
- int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
- if (count)
- LogMsg("Client application registered %d identical instances of service %##s port %u.",
- count+1, srv.c, mDNSVal16(request->u.servicereg.port));
- }
-
- LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START",
- request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
-
- // We need to unconditionally set request->terminate, because even if we didn't successfully
- // start any registrations right now, subsequent configuration changes may cause successful
- // registrations to be added, and we'll need to cancel them before freeing this memory.
- // We also need to set request->terminate first, before adding additional service instances,
- // because the uds_validatelists uses the request->terminate function pointer to determine
- // what kind of request this is, and therefore what kind of list validation is required.
- request->terminate = regservice_termination_callback;
-
- err = register_service_instance(request, &d);
+ request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr);
+ }
+
+ if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host))
+ { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); }
+ request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
+ request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
+
+ // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
+ // a port number of zero. When two instances of the protected client are allowed to run on one
+ // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
+ if (!mDNSIPPortIsZero(request->u.servicereg.port))
+ {
+ int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
+ if (count)
+ LogMsg("Client application registered %d identical instances of service %##s port %u.",
+ count+1, srv.c, mDNSVal16(request->u.servicereg.port));
+ }
+
+ LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+ request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
+
+ // We need to unconditionally set request->terminate, because even if we didn't successfully
+ // start any registrations right now, subsequent configuration changes may cause successful
+ // registrations to be added, and we'll need to cancel them before freeing this memory.
+ // We also need to set request->terminate first, before adding additional service instances,
+ // because the uds_validatelists uses the request->terminate function pointer to determine
+ // what kind of request this is, and therefore what kind of list validation is required.
+ request->terminate = regservice_termination_callback;
+
+ err = register_service_instance(request, &d);
#if 0
- err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError;
+ err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError;
#endif
- if (!err)
- {
- if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage);
-
- if (!*domain)
- {
- DNameListElem *ptr;
- // Note that we don't report errors for non-local, non-explicit domains
- for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next)
- if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid)
- register_service_instance(request, &ptr->name);
- }
- }
-
- return(err);
- }
+ if (!err)
+ {
+ if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage);
+
+ if (!*domain)
+ {
+ DNameListElem *ptr;
+ // Note that we don't report errors for non-local, non-explicit domains
+ for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next)
+ if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid)
+ register_service_instance(request, &ptr->name);
+ }
+ }
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -1728,462 +1730,462 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
#endif
mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
- {
- const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
- request_state *req = question->QuestionContext;
- reply_state *rep;
- (void)m; // Unused
-
- if (answer->rrtype != kDNSType_PTR)
- { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
-
- if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
- {
- if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
- {
- // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
- // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
- GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError);
- goto bonjourbrowserhack;
- }
-
- LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
- req->sd, answer->name->c, answer->rdata->u.name.c);
- return;
- }
+ {
+ const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
+ request_state *req = question->QuestionContext;
+ reply_state *rep;
+ (void)m; // Unused
+
+ if (answer->rrtype != kDNSType_PTR)
+ { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
+
+ if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
+ {
+ if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
+ {
+ // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+ // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+ GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError);
+ goto bonjourbrowserhack;
+ }
+
+ LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+ req->sd, answer->name->c, answer->rdata->u.name.c);
+ return;
+ }
bonjourbrowserhack:
- LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
- req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
- mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
+ LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
+ req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
+ mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
- append_reply(req, rep);
- }
+ append_reply(req, rep);
+ }
mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d)
- {
- browser_t *b, *p;
- mStatus err;
-
- for (p = info->u.browser.browsers; p; p = p->next)
- {
- if (SameDomainName(&p->domain, d))
- { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; }
- }
-
- b = mallocL("browser_t", sizeof(*b));
- if (!b) return mStatus_NoMemoryErr;
- AssignDomainName(&b->domain, d);
- err = mDNS_StartBrowse(&mDNSStorage, &b->q,
- &info->u.browser.regtype, d, info->u.browser.interface_id, info->u.browser.ForceMCast, FoundInstance, info);
- if (err)
- {
- LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
- freeL("browser_t/add_domain_to_browser", b);
- }
- else
- {
- b->next = info->u.browser.browsers;
- info->u.browser.browsers = b;
- LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c);
- if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
- {
- domainname tmp;
- ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
- LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
- external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
- }
- }
- return err;
- }
+ {
+ browser_t *b, *p;
+ mStatus err;
+
+ for (p = info->u.browser.browsers; p; p = p->next)
+ {
+ if (SameDomainName(&p->domain, d))
+ { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; }
+ }
+
+ b = mallocL("browser_t", sizeof(*b));
+ if (!b) return mStatus_NoMemoryErr;
+ AssignDomainName(&b->domain, d);
+ err = mDNS_StartBrowse(&mDNSStorage, &b->q,
+ &info->u.browser.regtype, d, info->u.browser.interface_id, info->u.browser.ForceMCast, FoundInstance, info);
+ if (err)
+ {
+ LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
+ freeL("browser_t/add_domain_to_browser", b);
+ }
+ else
+ {
+ b->next = info->u.browser.browsers;
+ info->u.browser.browsers = b;
+ LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c);
+ if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ domainname tmp;
+ ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
+ LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
+ external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
+ }
+ }
+ return err;
+ }
mDNSlocal void browse_termination_callback(request_state *info)
- {
- while (info->u.browser.browsers)
- {
- browser_t *ptr = info->u.browser.browsers;
-
- if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
- {
- domainname tmp;
- ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
- LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
- external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
- }
-
- info->u.browser.browsers = ptr->next;
- LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c);
- mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result
- freeL("browser_t/browse_termination_callback", ptr);
- }
- }
+ {
+ while (info->u.browser.browsers)
+ {
+ browser_t *ptr = info->u.browser.browsers;
+
+ if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ domainname tmp;
+ ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
+ LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
+ external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
+ }
+
+ info->u.browser.browsers = ptr->next;
+ LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c);
+ mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result
+ freeL("browser_t/browse_termination_callback", ptr);
+ }
+ }
mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *const d, const mDNSBool add)
- {
- request_state *request;
- debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c);
+ {
+ request_state *request;
+ debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c);
#if APPLE_OSX_mDNSResponder
- machserver_automatic_browse_domain_changed(&d->name, add);
+ machserver_automatic_browse_domain_changed(&d->name, add);
#endif // APPLE_OSX_mDNSResponder
- for (request = all_requests; request; request = request->next)
- {
- if (request->terminate != browse_termination_callback) continue; // Not a browse operation
- if (!request->u.browser.default_domain) continue; // Not an auto-browse operation
- if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
- {
- browser_t **ptr = &request->u.browser.browsers;
- while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
- if (add)
- {
- // If we don't already have this domain in our list for this browse operation, add it now
- if (!*ptr) add_domain_to_browser(request, &d->name);
- else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name);
- }
- else
- {
- if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name);
- else
- {
- DNameListElem *p;
- for (p = AutoBrowseDomains; p; p=p->next)
- if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
- if (SameDomainName(&d->name, &p->name)) break;
- if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name);
- else
- {
- browser_t *rem = *ptr;
- *ptr = (*ptr)->next;
- mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
- freeL("browser_t/udsserver_automatic_browse_domain_changed", rem);
- }
- }
- }
- }
- }
- }
+ for (request = all_requests; request; request = request->next)
+ {
+ if (request->terminate != browse_termination_callback) continue; // Not a browse operation
+ if (!request->u.browser.default_domain) continue; // Not an auto-browse operation
+ if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+ {
+ browser_t **ptr = &request->u.browser.browsers;
+ while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+ if (add)
+ {
+ // If we don't already have this domain in our list for this browse operation, add it now
+ if (!*ptr) add_domain_to_browser(request, &d->name);
+ else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name);
+ }
+ else
+ {
+ if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name);
+ else
+ {
+ DNameListElem *p;
+ for (p = AutoBrowseDomains; p; p=p->next)
+ if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+ if (SameDomainName(&d->name, &p->name)) break;
+ if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name);
+ else
+ {
+ browser_t *rem = *ptr;
+ *ptr = (*ptr)->next;
+ mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
+ freeL("browser_t/udsserver_automatic_browse_domain_changed", rem);
+ }
+ }
+ }
+ }
+ }
+ }
mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
- {
- (void)m; // unused
- if (result == mStatus_MemFree)
- {
- // On shutdown, mDNS_Close automatically deregisters all records
- // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
- // from the LocalDomainEnumRecords list, we do this here before we free the memory.
- // (This should actually no longer be necessary, now that we do the proper cleanup in
- // udsserver_exit. To confirm this, we'll log an error message if we do find a record that
- // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.)
- ARListElem **ptr = &LocalDomainEnumRecords;
- while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
- if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); }
- mDNSPlatformMemFree(rr->RecordContext);
- }
- }
+ {
+ (void)m; // unused
+ if (result == mStatus_MemFree)
+ {
+ // On shutdown, mDNS_Close automatically deregisters all records
+ // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
+ // from the LocalDomainEnumRecords list, we do this here before we free the memory.
+ // (This should actually no longer be necessary, now that we do the proper cleanup in
+ // udsserver_exit. To confirm this, we'll log an error message if we do find a record that
+ // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.)
+ ARListElem **ptr = &LocalDomainEnumRecords;
+ while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
+ if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); }
+ mDNSPlatformMemFree(rr->RecordContext);
+ }
+ }
// RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in
// "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records.
// We may want to turn the common code into a subroutine.
mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
- {
- // allocate/register legacy and non-legacy _browse PTR record
- mStatus err;
- ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr));
-
- debugf("Incrementing %s refcount for %##s",
- (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
- (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
- (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
-
- mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
- MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
- AppendDNSNameString (&ptr->ar.namestorage, "local");
- AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
- err = mDNS_Register(m, &ptr->ar);
- if (err)
- {
- LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err);
- mDNSPlatformMemFree(ptr);
- }
- else
- {
- ptr->next = LocalDomainEnumRecords;
- LocalDomainEnumRecords = ptr;
- }
- }
+ {
+ // allocate/register legacy and non-legacy _browse PTR record
+ mStatus err;
+ ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr));
+
+ debugf("Incrementing %s refcount for %##s",
+ (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
+ (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
+ (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+ mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
+ MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
+ AppendDNSNameString (&ptr->ar.namestorage, "local");
+ AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
+ err = mDNS_Register(m, &ptr->ar);
+ if (err)
+ {
+ LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err);
+ mDNSPlatformMemFree(ptr);
+ }
+ else
+ {
+ ptr->next = LocalDomainEnumRecords;
+ LocalDomainEnumRecords = ptr;
+ }
+ }
mDNSlocal void DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
- {
- ARListElem **ptr = &LocalDomainEnumRecords;
- domainname lhs; // left-hand side of PTR, for comparison
-
- debugf("Decrementing %s refcount for %##s",
- (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
- (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
- (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
-
- MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]);
- AppendDNSNameString (&lhs, "local");
-
- while (*ptr)
- {
- if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs))
- {
- ARListElem *rem = *ptr;
- *ptr = (*ptr)->next;
- mDNS_Deregister(m, &rem->ar);
- return;
- }
- else ptr = &(*ptr)->next;
- }
- }
+ {
+ ARListElem **ptr = &LocalDomainEnumRecords;
+ domainname lhs; // left-hand side of PTR, for comparison
+
+ debugf("Decrementing %s refcount for %##s",
+ (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
+ (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
+ (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+ MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]);
+ AppendDNSNameString (&lhs, "local");
+
+ while (*ptr)
+ {
+ if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs))
+ {
+ ARListElem *rem = *ptr;
+ *ptr = (*ptr)->next;
+ mDNS_Deregister(m, &rem->ar);
+ return;
+ }
+ else ptr = &(*ptr)->next;
+ }
+ }
mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
- {
- DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem));
- if (!new) { LogMsg("ERROR: malloc"); return; }
- AssignDomainName(&new->name, name);
- new->uid = uid;
- new->next = AutoBrowseDomains;
- AutoBrowseDomains = new;
- udsserver_automatic_browse_domain_changed(new, mDNStrue);
- }
+ {
+ DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem));
+ if (!new) { LogMsg("ERROR: malloc"); return; }
+ AssignDomainName(&new->name, name);
+ new->uid = uid;
+ new->next = AutoBrowseDomains;
+ AutoBrowseDomains = new;
+ udsserver_automatic_browse_domain_changed(new, mDNStrue);
+ }
mDNSlocal void RmvAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
- {
- DNameListElem **p = &AutoBrowseDomains;
- while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next;
- if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c);
- else
- {
- DNameListElem *ptr = *p;
- *p = ptr->next;
- udsserver_automatic_browse_domain_changed(ptr, mDNSfalse);
- mDNSPlatformMemFree(ptr);
- }
- }
+ {
+ DNameListElem **p = &AutoBrowseDomains;
+ while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next;
+ if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c);
+ else
+ {
+ DNameListElem *ptr = *p;
+ *p = ptr->next;
+ udsserver_automatic_browse_domain_changed(ptr, mDNSfalse);
+ mDNSPlatformMemFree(ptr);
+ }
+ }
mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNSBool add)
- {
- DNameListElem *d;
- for (d = browseDomains; d; d = d->next)
- {
- if (add)
- {
- RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
- AddAutoBrowseDomain(d->uid, &d->name);
- }
- else
- {
- DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
- RmvAutoBrowseDomain(d->uid, &d->name);
- }
- }
- }
+ {
+ DNameListElem *d;
+ for (d = browseDomains; d; d = d->next)
+ {
+ if (add)
+ {
+ RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+ AddAutoBrowseDomain(d->uid, &d->name);
+ }
+ else
+ {
+ DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+ RmvAutoBrowseDomain(d->uid, &d->name);
+ }
+ }
+ }
mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
- {
- int num_autoname = 0;
- request_state *req;
- for (req = all_requests; req; req = req->next)
- if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname)
- num_autoname++;
-
- // If DeviceInfo record is currently registered, see if we need to deregister it
- if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered)
- if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c))
- {
- LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name);
- mDNS_Deregister(m, &m->DeviceInfo);
- }
-
- // If DeviceInfo record is not currently registered, see if we need to register it
- if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
- if (num_autoname > 0)
- {
- mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
- mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
- ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
- mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
- mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
- m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string
- m->DeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string
- LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name);
- mDNS_Register(m, &m->DeviceInfo);
- }
- }
+ {
+ int num_autoname = 0;
+ request_state *req;
+ for (req = all_requests; req; req = req->next)
+ if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname)
+ num_autoname++;
+
+ // If DeviceInfo record is currently registered, see if we need to deregister it
+ if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered)
+ if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c))
+ {
+ LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name);
+ mDNS_Deregister(m, &m->DeviceInfo);
+ }
+
+ // If DeviceInfo record is not currently registered, see if we need to register it
+ if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
+ if (num_autoname > 0)
+ {
+ mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
+ mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
+ ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
+ mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
+ mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
+ m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string
+ m->DeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string
+ LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name);
+ mDNS_Register(m, &m->DeviceInfo);
+ }
+ }
mDNSexport void udsserver_handle_configchange(mDNS *const m)
- {
- request_state *req;
- service_instance *ptr;
- DNameListElem *RegDomains = NULL;
- DNameListElem *BrowseDomains = NULL;
- DNameListElem *p;
-
- UpdateDeviceInfoRecord(m);
-
- // For autoname services, see if the default service name has changed, necessitating an automatic update
- for (req = all_requests; req; req = req->next)
- if (req->terminate == regservice_termination_callback)
- if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c))
- {
- req->u.servicereg.name = m->nicelabel;
- for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
- {
- ptr->renameonmemfree = 1;
- if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs);
- LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c);
- if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid))
- regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately
- }
- }
-
- // Let the platform layer get the current DNS information
- mDNS_Lock(m);
- mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains);
- mDNS_Unlock(m);
-
- // Any automatic registration domains are also implicitly automatic browsing domains
- if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first
- if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list
-
- // Add any new domains not already in our AutoRegistrationDomains list
- for (p=RegDomains; p; p=p->next)
- {
- DNameListElem **pp = &AutoRegistrationDomains;
- while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next;
- if (!*pp) // If not found in our existing list, this is a new default registration domain
- {
- RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration);
- udsserver_default_reg_domain_changed(p, mDNStrue);
- }
- else // else found same domainname in both old and new lists, so no change, just delete old copy
- {
- DNameListElem *del = *pp;
- *pp = (*pp)->next;
- mDNSPlatformMemFree(del);
- }
- }
-
- // Delete any domains in our old AutoRegistrationDomains list that are now gone
- while (AutoRegistrationDomains)
- {
- DNameListElem *del = AutoRegistrationDomains;
- AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST,
- DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration);
- udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed()
- mDNSPlatformMemFree(del);
- }
-
- // Now we have our new updated automatic registration domain list
- AutoRegistrationDomains = RegDomains;
-
- // Add new browse domains to internal list
- if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue);
-
- // Remove old browse domains from internal list
- if (SCPrefBrowseDomains)
- {
- SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse);
- while (SCPrefBrowseDomains)
- {
- DNameListElem *fptr = SCPrefBrowseDomains;
- SCPrefBrowseDomains = SCPrefBrowseDomains->next;
- mDNSPlatformMemFree(fptr);
- }
- }
-
- // Replace the old browse domains array with the new array
- SCPrefBrowseDomains = BrowseDomains;
- }
+ {
+ request_state *req;
+ service_instance *ptr;
+ DNameListElem *RegDomains = NULL;
+ DNameListElem *BrowseDomains = NULL;
+ DNameListElem *p;
+
+ UpdateDeviceInfoRecord(m);
+
+ // For autoname services, see if the default service name has changed, necessitating an automatic update
+ for (req = all_requests; req; req = req->next)
+ if (req->terminate == regservice_termination_callback)
+ if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c))
+ {
+ req->u.servicereg.name = m->nicelabel;
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ {
+ ptr->renameonmemfree = 1;
+ if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs);
+ LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c);
+ if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid))
+ regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately
+ }
+ }
+
+ // Let the platform layer get the current DNS information
+ mDNS_Lock(m);
+ mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains);
+ mDNS_Unlock(m);
+
+ // Any automatic registration domains are also implicitly automatic browsing domains
+ if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first
+ if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list
+
+ // Add any new domains not already in our AutoRegistrationDomains list
+ for (p=RegDomains; p; p=p->next)
+ {
+ DNameListElem **pp = &AutoRegistrationDomains;
+ while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next;
+ if (!*pp) // If not found in our existing list, this is a new default registration domain
+ {
+ RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration);
+ udsserver_default_reg_domain_changed(p, mDNStrue);
+ }
+ else // else found same domainname in both old and new lists, so no change, just delete old copy
+ {
+ DNameListElem *del = *pp;
+ *pp = (*pp)->next;
+ mDNSPlatformMemFree(del);
+ }
+ }
+
+ // Delete any domains in our old AutoRegistrationDomains list that are now gone
+ while (AutoRegistrationDomains)
+ {
+ DNameListElem *del = AutoRegistrationDomains;
+ AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST,
+ DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration);
+ udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed()
+ mDNSPlatformMemFree(del);
+ }
+
+ // Now we have our new updated automatic registration domain list
+ AutoRegistrationDomains = RegDomains;
+
+ // Add new browse domains to internal list
+ if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue);
+
+ // Remove old browse domains from internal list
+ if (SCPrefBrowseDomains)
+ {
+ SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse);
+ while (SCPrefBrowseDomains)
+ {
+ DNameListElem *fptr = SCPrefBrowseDomains;
+ SCPrefBrowseDomains = SCPrefBrowseDomains->next;
+ mDNSPlatformMemFree(fptr);
+ }
+ }
+
+ // Replace the old browse domains array with the new array
+ SCPrefBrowseDomains = BrowseDomains;
+ }
mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const ResourceRecord *const answer, QC_result AddRecord)
- {
- (void)m; // unused;
- (void)q; // unused
+ {
+ (void)m; // unused;
+ (void)q; // unused
- LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s",
- AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c);
+ LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s",
+ AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c);
- if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name);
- else RmvAutoBrowseDomain(0, &answer->rdata->u.name);
- }
+ if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name);
+ else RmvAutoBrowseDomain(0, &answer->rdata->u.name);
+ }
mDNSlocal mStatus handle_browse_request(request_state *request)
- {
- char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
- domainname typedn, d, temp;
- mDNSs32 NumSubTypes;
- mStatus err = mStatus_NoError;
-
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
-
- if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr);
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
-
- request->flags = flags;
- typedn.c[0] = 0;
- NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
- if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr);
- if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr);
-
- if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
-
- if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr);
- // For over-long service types, we only allow domain "local"
- if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local.");
-
- // Set up browser info
- request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
- request->u.browser.interface_id = InterfaceID;
- AssignDomainName(&request->u.browser.regtype, &typedn);
- request->u.browser.default_domain = !domain[0];
- request->u.browser.browsers = NULL;
-
- LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START",
- request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain);
-
- // We need to unconditionally set request->terminate, because even if we didn't successfully
- // start any browses right now, subsequent configuration changes may cause successful
- // browses to be added, and we'll need to cancel them before freeing this memory.
- request->terminate = browse_termination_callback;
-
- if (domain[0])
- {
- if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr);
- err = add_domain_to_browser(request, &d);
+ {
+ char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname typedn, d, temp;
+ mDNSs32 NumSubTypes;
+ mStatus err = mStatus_NoError;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr);
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
+
+ request->flags = flags;
+ typedn.c[0] = 0;
+ NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
+ if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr);
+ if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr);
+
+ if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
+
+ if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr);
+ // For over-long service types, we only allow domain "local"
+ if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local.");
+
+ // Set up browser info
+ request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ request->u.browser.interface_id = InterfaceID;
+ AssignDomainName(&request->u.browser.regtype, &typedn);
+ request->u.browser.default_domain = !domain[0];
+ request->u.browser.browsers = NULL;
+
+ LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START",
+ request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain);
+
+ // We need to unconditionally set request->terminate, because even if we didn't successfully
+ // start any browses right now, subsequent configuration changes may cause successful
+ // browses to be added, and we'll need to cancel them before freeing this memory.
+ request->terminate = browse_termination_callback;
+
+ if (domain[0])
+ {
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr);
+ err = add_domain_to_browser(request, &d);
#if 0
- err = AuthorizedDomain(request, &d, AutoBrowseDomains) ? add_domain_to_browser(request, &d) : mStatus_NoError;
+ err = AuthorizedDomain(request, &d, AutoBrowseDomains) ? add_domain_to_browser(request, &d) : mStatus_NoError;
#endif
- }
- else
- {
- DNameListElem *sdom;
- for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next)
- if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid)
- {
- err = add_domain_to_browser(request, &sdom->name);
- if (err)
- {
- if (SameDomainName(&sdom->name, &localdomain)) break;
- else err = mStatus_NoError; // suppress errors for non-local "default" domains
- }
- }
- }
-
- return(err);
- }
+ }
+ else
+ {
+ DNameListElem *sdom;
+ for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next)
+ if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid)
+ {
+ err = add_domain_to_browser(request, &sdom->name);
+ if (err)
+ {
+ if (SameDomainName(&sdom->name, &localdomain)) break;
+ else err = mStatus_NoError; // suppress errors for non-local "default" domains
+ }
+ }
+ }
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -2192,169 +2194,169 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
#endif
mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
- {
- size_t len = 0;
- char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
- char *data;
- reply_state *rep;
- request_state *req = question->QuestionContext;
- (void)m; // Unused
-
- LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
-
- if (!AddRecord)
- {
- if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL;
- if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL;
- return;
- }
-
- if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer;
- if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer;
-
- if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers
-
- ConvertDomainNameToCString(answer->name, fullname);
- ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target);
-
- // calculate reply length
- len += sizeof(DNSServiceFlags);
- len += sizeof(mDNSu32); // interface index
- len += sizeof(DNSServiceErrorType);
- len += strlen(fullname) + 1;
- len += strlen(target) + 1;
- len += 2 * sizeof(mDNSu16); // port, txtLen
- len += req->u.resolve.txt->rdlength;
-
- // allocate/init reply header
- rep = create_reply(resolve_reply_op, len, req);
- rep->rhdr->flags = dnssd_htonl(0);
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
- rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
-
- data = (char *)&rep->rhdr[1];
-
- // write reply data to message
- put_string(fullname, &data);
- put_string(target, &data);
- *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0];
- *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1];
- put_uint16(req->u.resolve.txt->rdlength, &data);
- put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data);
-
- LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port));
- append_reply(req, rep);
- }
+ {
+ size_t len = 0;
+ char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
+ char *data;
+ reply_state *rep;
+ request_state *req = question->QuestionContext;
+ (void)m; // Unused
+
+ LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+ if (!AddRecord)
+ {
+ if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL;
+ if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL;
+ return;
+ }
+
+ if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer;
+ if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer;
+
+ if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers
+
+ ConvertDomainNameToCString(answer->name, fullname);
+ ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target);
+
+ // calculate reply length
+ len += sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(fullname) + 1;
+ len += strlen(target) + 1;
+ len += 2 * sizeof(mDNSu16); // port, txtLen
+ len += req->u.resolve.txt->rdlength;
+
+ // allocate/init reply header
+ rep = create_reply(resolve_reply_op, len, req);
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
+ rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+ data = (char *)&rep->rhdr[1];
+
+ // write reply data to message
+ put_string(fullname, &data);
+ put_string(target, &data);
+ *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0];
+ *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1];
+ put_uint16(req->u.resolve.txt->rdlength, &data);
+ put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data);
+
+ LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port));
+ append_reply(req, rep);
+ }
mDNSlocal void resolve_termination_callback(request_state *request)
- {
- LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c);
- mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
- mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
- if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname);
- }
+ {
+ LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c);
+ mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
+ mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+ if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname);
+ }
mDNSlocal mStatus handle_resolve_request(request_state *request)
- {
- char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
- domainname fqdn;
- mStatus err;
-
- // extract the data from the message
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID;
- mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P);
-
-
- request->flags = flags;
- if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny;
-
- InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID)
- { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
-
- if (get_string(&request->msgptr, request->msgend, name, 256) < 0 ||
- get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
- { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
-
- if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+ {
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname fqdn;
+ mStatus err;
+
+ // extract the data from the message
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID;
+ mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P);
+
+
+ request->flags = flags;
+ if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny;
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+ if (get_string(&request->msgptr, request->msgend, name, 256) < 0 ||
+ get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
{ LogMsg("ERROR: handle_resolve_request bad '%s' '%s' '%s'", name, regtype, domain); return(mStatus_BadParamErr); }
- mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve));
-
- // format questions
- request->u.resolve.qsrv.InterfaceID = InterfaceID;
- request->u.resolve.qsrv.Target = zeroAddr;
- AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn);
- request->u.resolve.qsrv.qtype = kDNSType_SRV;
- request->u.resolve.qsrv.qclass = kDNSClass_IN;
- request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
- request->u.resolve.qsrv.ExpectUnique = mDNStrue;
- request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
- request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
- request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
- request->u.resolve.qsrv.SearchListIndex = 0;
- request->u.resolve.qsrv.AppendSearchDomains = 0;
- request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
- request->u.resolve.qsrv.TimeoutQuestion = 0;
- request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
- request->u.resolve.qsrv.qnameOrig = mDNSNULL;
- request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
- request->u.resolve.qsrv.QuestionContext = request;
-
- request->u.resolve.qtxt.InterfaceID = InterfaceID;
- request->u.resolve.qtxt.Target = zeroAddr;
- AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn);
- request->u.resolve.qtxt.qtype = kDNSType_TXT;
- request->u.resolve.qtxt.qclass = kDNSClass_IN;
- request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
- request->u.resolve.qtxt.ExpectUnique = mDNStrue;
- request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
- request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
- request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
- request->u.resolve.qtxt.SearchListIndex = 0;
- request->u.resolve.qtxt.AppendSearchDomains = 0;
- request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
- request->u.resolve.qtxt.TimeoutQuestion = 0;
- request->u.resolve.qtxt.WakeOnResolve = 0;
- request->u.resolve.qtxt.qnameOrig = mDNSNULL;
- request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
- request->u.resolve.qtxt.QuestionContext = request;
-
- request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
-
- request->u.resolve.external_advertise = mDNSfalse;
+ mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve));
+
+ // format questions
+ request->u.resolve.qsrv.InterfaceID = InterfaceID;
+ request->u.resolve.qsrv.Target = zeroAddr;
+ AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn);
+ request->u.resolve.qsrv.qtype = kDNSType_SRV;
+ request->u.resolve.qsrv.qclass = kDNSClass_IN;
+ request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.resolve.qsrv.ExpectUnique = mDNStrue;
+ request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
+ request->u.resolve.qsrv.SearchListIndex = 0;
+ request->u.resolve.qsrv.AppendSearchDomains = 0;
+ request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qsrv.TimeoutQuestion = 0;
+ request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
+ request->u.resolve.qsrv.qnameOrig = mDNSNULL;
+ request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
+ request->u.resolve.qsrv.QuestionContext = request;
+
+ request->u.resolve.qtxt.InterfaceID = InterfaceID;
+ request->u.resolve.qtxt.Target = zeroAddr;
+ AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn);
+ request->u.resolve.qtxt.qtype = kDNSType_TXT;
+ request->u.resolve.qtxt.qclass = kDNSClass_IN;
+ request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.resolve.qtxt.ExpectUnique = mDNStrue;
+ request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
+ request->u.resolve.qtxt.SearchListIndex = 0;
+ request->u.resolve.qtxt.AppendSearchDomains = 0;
+ request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qtxt.TimeoutQuestion = 0;
+ request->u.resolve.qtxt.WakeOnResolve = 0;
+ request->u.resolve.qtxt.qnameOrig = mDNSNULL;
+ request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
+ request->u.resolve.qtxt.QuestionContext = request;
+
+ request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
+
+ request->u.resolve.external_advertise = mDNSfalse;
#if 0
- if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError);
+ if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError);
#endif
- // ask the questions
- LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c);
- err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
- if (!err)
- {
- err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
- if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
- else
- {
- request->terminate = resolve_termination_callback;
- // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve.
- if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P)))
- {
- request->u.resolve.external_advertise = mDNStrue;
- LogInfo("handle_resolve_request: calling external_start_resolving_service()");
- external_start_resolving_service(&fqdn);
- }
- }
- }
-
- return(err);
- }
+ // ask the questions
+ LogOperation("%3d: DNSServiceResolve(%X %d %##s) START", request->sd, flags, interfaceIndex, request->u.resolve.qsrv.qname.c);
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
+ if (!err)
+ {
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
+ if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+ else
+ {
+ request->terminate = resolve_termination_callback;
+ // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve.
+ if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ request->u.resolve.external_advertise = mDNStrue;
+ LogInfo("handle_resolve_request: calling external_start_resolving_service()");
+ external_start_resolving_service(&fqdn);
+ }
+ }
+ }
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -2367,659 +2369,659 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
// the mDNSCore operation if the client dies or closes its socket.
-// Returns -1 to tell the caller that it should not try to reissue the query anymore
+// Returns -1 to tell the caller that it should not try to reissue the query anymore
// Returns 1 on successfully appending a search domain and the caller should reissue the new query
// Returns 0 when there are no more search domains and the caller should reissue the query
mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question)
- {
- domainname *sd;
- mStatus err;
-
- // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
- // the domains and should try the single label query directly on the wire.
- if (question->SearchListIndex == -1)
- {
- LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
- return -1;
- }
-
- if (!question->AppendSearchDomains)
- {
- LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
- return -1;
- }
-
- // Save the original name, before we modify them below.
- if (!question->qnameOrig)
- {
- question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname));
- if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; }
- question->qnameOrig->c[0] = 0;
- AssignDomainName(question->qnameOrig, &question->qname);
- LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
- }
-
- sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
- // We use -1 to indicate that we have searched all the domains and should try the single label
- // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
- if (question->SearchListIndex == -1)
- {
- LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
- return -1;
- }
-
- // Not a common case. Perhaps, we should try the next search domain if it exceeds ?
- if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
- {
- LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
- return -1;
- }
-
- // if there are no more search domains and we have already tried this question
- // without appending search domains, then we are done.
- if (!sd && !ApplySearchDomainsFirst(question))
- {
- LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
- return -1;
- }
-
- // Stop the question before changing the name as negative cache entries could be pointing at this question.
- // Even if we don't change the question in the case of returning 0, the caller is going to restart the
- // question.
- err = mDNS_StopQuery(&mDNSStorage, question);
- if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
-
- AssignDomainName(&question->qname, question->qnameOrig);
- if (sd)
- {
- AppendDomainName(&question->qname, sd);
- LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
- return 1;
- }
-
- // Try the question as single label
- LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
- return 0;
- }
+ {
+ domainname *sd;
+ mStatus err;
+
+ // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
+ // the domains and should try the single label query directly on the wire.
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ if (!question->AppendSearchDomains)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Save the original name, before we modify them below.
+ if (!question->qnameOrig)
+ {
+ question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname));
+ if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; }
+ question->qnameOrig->c[0] = 0;
+ AssignDomainName(question->qnameOrig, &question->qname);
+ LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
+ }
+
+ sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
+ // We use -1 to indicate that we have searched all the domains and should try the single label
+ // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
+ return -1;
+ }
+
+ // Not a common case. Perhaps, we should try the next search domain if it exceeds ?
+ if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
+ return -1;
+ }
+
+ // if there are no more search domains and we have already tried this question
+ // without appending search domains, then we are done.
+ if (!sd && !ApplySearchDomainsFirst(question))
+ {
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Stop the question before changing the name as negative cache entries could be pointing at this question.
+ // Even if we don't change the question in the case of returning 0, the caller is going to restart the
+ // question.
+ err = mDNS_StopQuery(&mDNSStorage, question);
+ if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
+
+ AssignDomainName(&question->qname, question->qnameOrig);
+ if (sd)
+ {
+ AppendDomainName(&question->qname, sd);
+ LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
+ return 1;
+ }
+
+ // Try the question as single label
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
+ return 0;
+ }
#if APPLE_OSX_mDNSResponder
mDNSlocal mDNSBool DomainInSearchList(domainname *domain)
- {
- const SearchListElem *s;
- for (s=SearchList; s; s=s->next)
- if (SameDomainName(&s->domain, domain)) return mDNStrue;
- return mDNSfalse;
- }
+ {
+ const SearchListElem *s;
+ for (s=SearchList; s; s=s->next)
+ if (SameDomainName(&s->domain, domain)) return mDNStrue;
+ return mDNSfalse;
+ }
// Workaround for networks using Microsoft Active Directory using "local" as a private internal
// top-level domain
mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
- {
- extern domainname ActiveDirectoryPrimaryDomain;
- DNSQuestion **question2;
- #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
- #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
-
- question2 = mDNSNULL;
- if (request->hdr.op == query_request)
- question2 = &request->u.queryrecord.q2;
- else if (request->hdr.op == addrinfo_request)
- {
- if (q->qtype == kDNSType_A)
- question2 = &request->u.addrinfo.q42;
- else if (q->qtype == kDNSType_AAAA)
- question2 = &request->u.addrinfo.q62;
- }
- if (!question2)
- {
- LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- return mStatus_BadParamErr;
- }
-
- // Sanity check: If we already sent an additonal query, we don't need to send one more.
- //
- // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
- // is called to see whether a unicast query should be sent or not.
- //
- // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
- // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
- // send the additional query.
- //
- // Thus, it should not be called more than once.
- if (*question2)
- {
- LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
- return err;
- }
-
- if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
- if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
- {
- DNSQuestion *q2;
- int labels = CountLabels(&q->qname);
- q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
- if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
- *question2 = q2;
- *q2 = *q;
- q2->InterfaceID = mDNSInterface_Unicast;
- q2->ExpectUnique = mDNStrue;
- // If the query starts as a single label e.g., somehost, and we have search domains with .local,
- // queryrecord_result_callback calls this function when .local is appended to "somehost".
- // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
- // "somehost". We need to copy that information so that when we retry with a different search
- // domain e.g., mycompany.local, we get "somehost.mycompany.local".
- if (q->qnameOrig)
- {
- (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
- if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; }
- (*question2)->qnameOrig->c[0] = 0;
- AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
- LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
- }
- // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
- // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
- // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
- // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
- // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
- // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
- // for names in the "local" domain will be safely answered privately before they hit the root name servers.
- // Note that in the "my-small-company.local" example above there will typically be an SOA record for
- // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
- // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
- // of those, we don't want do the SOA check for the local
- if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname))
- {
- AssignDomainName(&q2->qname, &localdomain);
- q2->qtype = kDNSType_SOA;
- q2->LongLived = mDNSfalse;
- q2->ForceMCast = mDNSfalse;
- q2->ReturnIntermed = mDNStrue;
- // Don't append search domains for the .local SOA query
- q2->AppendSearchDomains = 0;
- q2->AppendLocalSearchDomains = 0;
- q2->RetryWithSearchDomains = mDNSfalse;
- q2->SearchListIndex = 0;
- q2->TimeoutQuestion = 0;
- }
- LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
- err = mDNS_StartQuery(&mDNSStorage, q2);
- if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
- }
- return(err);
- }
+ {
+ extern domainname ActiveDirectoryPrimaryDomain;
+ DNSQuestion **question2;
+ #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
+ #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
+
+ question2 = mDNSNULL;
+ if (request->hdr.op == query_request)
+ question2 = &request->u.queryrecord.q2;
+ else if (request->hdr.op == addrinfo_request)
+ {
+ if (q->qtype == kDNSType_A)
+ question2 = &request->u.addrinfo.q42;
+ else if (q->qtype == kDNSType_AAAA)
+ question2 = &request->u.addrinfo.q62;
+ }
+ if (!question2)
+ {
+ LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ return mStatus_BadParamErr;
+ }
+
+ // Sanity check: If we already sent an additonal query, we don't need to send one more.
+ //
+ // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
+ // is called to see whether a unicast query should be sent or not.
+ //
+ // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
+ // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
+ // send the additional query.
+ //
+ // Thus, it should not be called more than once.
+ if (*question2)
+ {
+ LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
+ return err;
+ }
+
+ if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
+ if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
+ {
+ DNSQuestion *q2;
+ int labels = CountLabels(&q->qname);
+ q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
+ if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
+ *question2 = q2;
+ *q2 = *q;
+ q2->InterfaceID = mDNSInterface_Unicast;
+ q2->ExpectUnique = mDNStrue;
+ // If the query starts as a single label e.g., somehost, and we have search domains with .local,
+ // queryrecord_result_callback calls this function when .local is appended to "somehost".
+ // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
+ // "somehost". We need to copy that information so that when we retry with a different search
+ // domain e.g., mycompany.local, we get "somehost.mycompany.local".
+ if (q->qnameOrig)
+ {
+ (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
+ if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; }
+ (*question2)->qnameOrig->c[0] = 0;
+ AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
+ LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
+ }
+ // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
+ // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
+ // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
+ // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
+ // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
+ // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
+ // for names in the "local" domain will be safely answered privately before they hit the root name servers.
+ // Note that in the "my-small-company.local" example above there will typically be an SOA record for
+ // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
+ // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
+ // of those, we don't want do the SOA check for the local
+ if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname))
+ {
+ AssignDomainName(&q2->qname, &localdomain);
+ q2->qtype = kDNSType_SOA;
+ q2->LongLived = mDNSfalse;
+ q2->ForceMCast = mDNSfalse;
+ q2->ReturnIntermed = mDNStrue;
+ // Don't append search domains for the .local SOA query
+ q2->AppendSearchDomains = 0;
+ q2->AppendLocalSearchDomains = 0;
+ q2->RetryWithSearchDomains = mDNSfalse;
+ q2->SearchListIndex = 0;
+ q2->TimeoutQuestion = 0;
+ }
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
+ err = mDNS_StartQuery(&mDNSStorage, q2);
+ if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
+ }
+ return(err);
+ }
#endif // APPLE_OSX_mDNSResponder
// This function tries to append a search domain if valid and possible. If so, returns true.
mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req)
- {
- int result;
- // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
- // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
- // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
- // RetryWithSearchDomains which may or may not be set.
- //
- // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
- // is a valid question for appending search domains, retry by appending domains
-
- if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains)
- {
- question->RetryWithSearchDomains = 0;
- result = AppendNewSearchDomain(m, question);
- // As long as the result is either zero or 1, we retry the question. If we exahaust the search
- // domains (result is zero) we try the original query (as it was before appending the search
- // domains) as such on the wire as a last resort if we have not tried them before. For queries
- // with more than one label, we have already tried them before appending search domains and
- // hence don't retry again
- if (result != -1)
- {
- mStatus err;
- err = mDNS_StartQuery(m, question);
- if (!err)
- {
- LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
- // If the result was zero, it meant that there are no search domains and we just retried the question
- // as a single label and we should not retry with search domains anymore.
- if (!result) question->SearchListIndex = -1;
- return mDNStrue;
- }
- else
- {
- LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
- // We have already stopped the query and could not restart. Reset the appropriate pointers
- // so that we don't call stop again when the question terminates
- question->QuestionContext = mDNSNULL;
- }
- }
- }
- else
- {
- LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
- }
- return mDNSfalse;
- }
+ {
+ int result;
+ // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
+ // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
+ // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
+ // RetryWithSearchDomains which may or may not be set.
+ //
+ // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
+ // is a valid question for appending search domains, retry by appending domains
+
+ if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains)
+ {
+ question->RetryWithSearchDomains = 0;
+ result = AppendNewSearchDomain(m, question);
+ // As long as the result is either zero or 1, we retry the question. If we exahaust the search
+ // domains (result is zero) we try the original query (as it was before appending the search
+ // domains) as such on the wire as a last resort if we have not tried them before. For queries
+ // with more than one label, we have already tried them before appending search domains and
+ // hence don't retry again
+ if (result != -1)
+ {
+ mStatus err;
+ err = mDNS_StartQuery(m, question);
+ if (!err)
+ {
+ LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
+ // If the result was zero, it meant that there are no search domains and we just retried the question
+ // as a single label and we should not retry with search domains anymore.
+ if (!result) question->SearchListIndex = -1;
+ return mDNStrue;
+ }
+ else
+ {
+ LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+ // We have already stopped the query and could not restart. Reset the appropriate pointers
+ // so that we don't call stop again when the question terminates
+ question->QuestionContext = mDNSNULL;
+ }
+ }
+ }
+ else
+ {
+ LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
+ }
+ return mDNSfalse;
+ }
mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
- {
- char name[MAX_ESCAPED_DOMAIN_NAME];
- request_state *req = question->QuestionContext;
- reply_state *rep;
- char *data;
- size_t len;
- DNSServiceErrorType error = kDNSServiceErr_NoError;
- DNSQuestion *q = mDNSNULL;
+ {
+ char name[MAX_ESCAPED_DOMAIN_NAME];
+ request_state *req = question->QuestionContext;
+ reply_state *rep;
+ char *data;
+ size_t len;
+ DNSServiceErrorType error = kDNSServiceErr_NoError;
+ DNSQuestion *q = mDNSNULL;
#if APPLE_OSX_mDNSResponder
- {
- // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
- // get any callbacks from the core after this.
- if (!req)
- {
- LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
- return;
- }
- if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
- q = &req->u.queryrecord.q;
- else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
- q = &req->u.addrinfo.q4;
- else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
- q = &req->u.addrinfo.q6;
-
- if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
- {
- mStatus err;
- domainname *orig = question->qnameOrig;
-
- LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
- mDNS_StopQuery(m, question);
- question->QuestionContext = mDNSNULL;
-
- // We got a negative response for the SOA record indicating that .local does not exist.
- // But we might have other search domains (that does not end in .local) that can be
- // appended to this question. In that case, we want to retry the question. Otherwise,
- // we don't want to try this question as unicast.
- if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
- {
- LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
- return;
- }
-
- // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
- //
- // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
- // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
- // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
- // SendAdditionalQuery as to how qnameOrig gets initialized.
- *question = *q;
- question->InterfaceID = mDNSInterface_Unicast;
- question->ExpectUnique = mDNStrue;
- question->qnameOrig = orig;
-
- LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
-
- // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
- // Hence, we need to set it explicitly here.
- question->QuestionContext = req;
- err = mDNS_StartQuery(m, question);
- if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
-
- // If we got a positive response to local SOA, then try the .local question as unicast
- if (answer->RecordType != kDNSRecordTypePacketNegative) return;
-
- // Fall through and get the next search domain. The question is pointing at .local
- // and we don't want to try that. Try the next search domain. Don't try with local
- // search domains for the unicast question anymore.
- //
- // Note: we started the question above which will be stopped immediately (never sent on the wire)
- // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
- // question has already started.
- question->AppendLocalSearchDomains = 0;
- }
-
- if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
- {
- // If we get a negative response to the unicast query that we sent above, retry after appending search domains
- // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here.
- // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
- // To keep things simple, we handle unicast ".local" separately here.
- LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
- if (RetryQuestionWithSearchDomains(m, question, req))
- return;
- if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
- {
- // If "local" is the last search domain, we need to stop the question so that we don't send the "local"
- // question on the wire as we got a negative response for the local SOA. But, we can't stop the question
- // yet as we may have to timeout the question (done by the "core") for which we need to leave the question
- // in the list. We leave it disabled so that it does not hit the wire.
- LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
- question->ThisQInterval = 0;
- }
- }
- // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
- // domains to append for "q2". In all cases, fall through and deliver the response
- }
+ {
+ // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
+ // get any callbacks from the core after this.
+ if (!req)
+ {
+ LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return;
+ }
+ if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
+ q = &req->u.queryrecord.q;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
+ q = &req->u.addrinfo.q4;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
+ q = &req->u.addrinfo.q6;
+
+ if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
+ {
+ mStatus err;
+ domainname *orig = question->qnameOrig;
+
+ LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+ mDNS_StopQuery(m, question);
+ question->QuestionContext = mDNSNULL;
+
+ // We got a negative response for the SOA record indicating that .local does not exist.
+ // But we might have other search domains (that does not end in .local) that can be
+ // appended to this question. In that case, we want to retry the question. Otherwise,
+ // we don't want to try this question as unicast.
+ if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+ {
+ LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+ return;
+ }
+
+ // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
+ //
+ // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+ // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+ // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+ // SendAdditionalQuery as to how qnameOrig gets initialized.
+ *question = *q;
+ question->InterfaceID = mDNSInterface_Unicast;
+ question->ExpectUnique = mDNStrue;
+ question->qnameOrig = orig;
+
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
+
+ // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
+ // Hence, we need to set it explicitly here.
+ question->QuestionContext = req;
+ err = mDNS_StartQuery(m, question);
+ if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+
+ // If we got a positive response to local SOA, then try the .local question as unicast
+ if (answer->RecordType != kDNSRecordTypePacketNegative) return;
+
+ // Fall through and get the next search domain. The question is pointing at .local
+ // and we don't want to try that. Try the next search domain. Don't try with local
+ // search domains for the unicast question anymore.
+ //
+ // Note: we started the question above which will be stopped immediately (never sent on the wire)
+ // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
+ // question has already started.
+ question->AppendLocalSearchDomains = 0;
+ }
+
+ if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+ {
+ // If we get a negative response to the unicast query that we sent above, retry after appending search domains
+ // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here.
+ // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
+ // To keep things simple, we handle unicast ".local" separately here.
+ LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req))
+ return;
+ if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
+ {
+ // If "local" is the last search domain, we need to stop the question so that we don't send the "local"
+ // question on the wire as we got a negative response for the local SOA. But, we can't stop the question
+ // yet as we may have to timeout the question (done by the "core") for which we need to leave the question
+ // in the list. We leave it disabled so that it does not hit the wire.
+ LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ question->ThisQInterval = 0;
+ }
+ }
+ // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
+ // domains to append for "q2". In all cases, fall through and deliver the response
+ }
#endif // APPLE_OSX_mDNSResponder
- if (answer->RecordType == kDNSRecordTypePacketNegative)
- {
- // If this question needs to be timed out and we have reached the stop time, mark
- // the error as timeout. It is possible that we might get a negative response from an
- // external DNS server at the same time when this question reaches its stop time. We
- // can't tell the difference as there is no indication in the callback. This should
- // be okay as we will be timing out this query anyway.
- mDNS_Lock(m);
- if (question->TimeoutQuestion)
- {
- if ((m->timenow - question->StopTime) >= 0)
- {
- LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
- error = kDNSServiceErr_Timeout;
- }
- }
- mDNS_Unlock(m);
- // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
- // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
- // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
- // server is going to assert that pretty much every single multicast name doesn't exist.
- //
- // If we are timing out this query, we need to deliver the negative answer to the application
- if (error != kDNSServiceErr_Timeout)
- {
- if (!answer->InterfaceID && IsLocalDomain(answer->name))
- {
- LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype));
- return;
- }
- error = kDNSServiceErr_NoSuchRecord;
- }
- AddRecord = mDNStrue;
- }
- // If we get a negative answer, try appending search domains. Don't append search domains
- // - if we are timing out this question
- // - if the negative response was received as a result of a multicast query
- // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
- if (error != kDNSServiceErr_Timeout)
- {
- if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
- {
- // If the original question did not end in .local, we did not send an SOA query
- // to figure out whether we should send an additional unicast query or not. If we just
- // appended .local, we need to see if we need to send an additional query. This should
- // normally happen just once because after we append .local, we ignore all negative
- // responses for .local above.
- LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
- if (RetryQuestionWithSearchDomains(m, question, req))
- {
- // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
- // be anywhere in the search domain list.
+ if (answer->RecordType == kDNSRecordTypePacketNegative)
+ {
+ // If this question needs to be timed out and we have reached the stop time, mark
+ // the error as timeout. It is possible that we might get a negative response from an
+ // external DNS server at the same time when this question reaches its stop time. We
+ // can't tell the difference as there is no indication in the callback. This should
+ // be okay as we will be timing out this query anyway.
+ mDNS_Lock(m);
+ if (question->TimeoutQuestion)
+ {
+ if ((m->timenow - question->StopTime) >= 0)
+ {
+ LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ error = kDNSServiceErr_Timeout;
+ }
+ }
+ mDNS_Unlock(m);
+ // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
+ // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
+ // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
+ // server is going to assert that pretty much every single multicast name doesn't exist.
+ //
+ // If we are timing out this query, we need to deliver the negative answer to the application
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!answer->InterfaceID && IsLocalDomain(answer->name))
+ {
+ LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype));
+ return;
+ }
+ error = kDNSServiceErr_NoSuchRecord;
+ }
+ AddRecord = mDNStrue;
+ }
+ // If we get a negative answer, try appending search domains. Don't append search domains
+ // - if we are timing out this question
+ // - if the negative response was received as a result of a multicast query
+ // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
+ {
+ // If the original question did not end in .local, we did not send an SOA query
+ // to figure out whether we should send an additional unicast query or not. If we just
+ // appended .local, we need to see if we need to send an additional query. This should
+ // normally happen just once because after we append .local, we ignore all negative
+ // responses for .local above.
+ LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req))
+ {
+ // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
+ // be anywhere in the search domain list.
#if APPLE_OSX_mDNSResponder
- mStatus err = mStatus_NoError;
- err = SendAdditionalQuery(question, req, err);
- if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
+ mStatus err = mStatus_NoError;
+ err = SendAdditionalQuery(question, req, err);
+ if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
#endif // APPLE_OSX_mDNSResponder
- return;
- }
- }
- }
-
- ConvertDomainNameToCString(answer->name, name);
-
- LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
- req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
- question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
-
- len = sizeof(DNSServiceFlags); // calculate reply data length
- len += sizeof(mDNSu32); // interface index
- len += sizeof(DNSServiceErrorType);
- len += strlen(name) + 1;
- len += 3 * sizeof(mDNSu16); // type, class, rdlen
- len += answer->rdlength;
- len += sizeof(mDNSu32); // TTL
-
- rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
-
- rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
- // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
- // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
- // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we
- // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
- // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
- // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
- // should not have existed to answer this question if the corresponding interface is not valid.
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
- rep->rhdr->error = dnssd_htonl(error);
-
- data = (char *)&rep->rhdr[1];
-
- put_string(name, &data);
- put_uint16(answer->rrtype, &data);
- put_uint16(answer->rrclass, &data);
- put_uint16(answer->rdlength, &data);
- // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
- // function just does a blind memory copy without regard to structures that may have holes in them.
- if (answer->rdlength)
- if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
- LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
- data += answer->rdlength;
- put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
-
- append_reply(req, rep);
- // Stop the question, if we just timed out
- if (error == kDNSServiceErr_Timeout)
- {
- mDNS_StopQuery(m, question);
- // Reset the pointers so that we don't call stop on termination
- question->QuestionContext = mDNSNULL;
- }
+ return;
+ }
+ }
+ }
+
+ ConvertDomainNameToCString(answer->name, name);
+
+ LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
+ req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
+ question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+ len = sizeof(DNSServiceFlags); // calculate reply data length
+ len += sizeof(mDNSu32); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(name) + 1;
+ len += 3 * sizeof(mDNSu16); // type, class, rdlen
+ len += answer->rdlength;
+ len += sizeof(mDNSu32); // TTL
+
+ rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
+
+ rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
+ // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+ // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+ // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we
+ // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+ // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+ // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+ // should not have existed to answer this question if the corresponding interface is not valid.
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
+ rep->rhdr->error = dnssd_htonl(error);
+
+ data = (char *)&rep->rhdr[1];
+
+ put_string(name, &data);
+ put_uint16(answer->rrtype, &data);
+ put_uint16(answer->rrclass, &data);
+ put_uint16(answer->rdlength, &data);
+ // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
+ // function just does a blind memory copy without regard to structures that may have holes in them.
+ if (answer->rdlength)
+ if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
+ LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
+ data += answer->rdlength;
+ put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
+
+ append_reply(req, rep);
+ // Stop the question, if we just timed out
+ if (error == kDNSServiceErr_Timeout)
+ {
+ mDNS_StopQuery(m, question);
+ // Reset the pointers so that we don't call stop on termination
+ question->QuestionContext = mDNSNULL;
+ }
#if APPLE_OSX_mDNSResponder
#if ! NO_WCF
- CHECK_WCF_FUNCTION(WCFIsServerRunning)
- {
- struct xucred x;
- socklen_t xucredlen = sizeof(x);
-
- if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
- {
- if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
- (x.cr_version == XUCRED_VERSION))
- {
- struct sockaddr_storage addr;
- const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
- addr.ss_len = 0;
- if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
- {
- if (answer->rrtype == kDNSType_A)
- {
- struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
- sin->sin_port = 0;
- if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
- LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed");
- else
- {
- addr.ss_len = sizeof (struct sockaddr_in);
- addr.ss_family = AF_INET;
- }
- }
- else if (answer->rrtype == kDNSType_AAAA)
- {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
- sin6->sin6_port = 0;
- if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
- LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed");
- else
- {
- addr.ss_len = sizeof (struct sockaddr_in6);
- addr.ss_family = AF_INET6;
- }
- }
- if (addr.ss_len)
- {
- debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
- CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
- {
- WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
- }
- }
- }
- else if (answer->rrtype == kDNSType_CNAME)
- {
- domainname cname;
- char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
- if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
- LogMsg("queryrecord_result_callback: WCF CNAME putRData failed");
- else
- {
- ConvertDomainNameToCString(&cname, cname_cstr);
- CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
- {
- WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
- }
- }
- }
- }
- else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED");
- }
- }
+ CHECK_WCF_FUNCTION(WCFIsServerRunning)
+ {
+ struct xucred x;
+ socklen_t xucredlen = sizeof(x);
+
+ if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
+ {
+ if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
+ (x.cr_version == XUCRED_VERSION))
+ {
+ struct sockaddr_storage addr;
+ const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
+ addr.ss_len = 0;
+ if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
+ {
+ if (answer->rrtype == kDNSType_A)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+ sin->sin_port = 0;
+ if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
+ LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed");
+ else
+ {
+ addr.ss_len = sizeof (struct sockaddr_in);
+ addr.ss_family = AF_INET;
+ }
+ }
+ else if (answer->rrtype == kDNSType_AAAA)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+ sin6->sin6_port = 0;
+ if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
+ LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed");
+ else
+ {
+ addr.ss_len = sizeof (struct sockaddr_in6);
+ addr.ss_family = AF_INET6;
+ }
+ }
+ if (addr.ss_len)
+ {
+ debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
+ CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+ {
+ WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
+ }
+ }
+ }
+ else if (answer->rrtype == kDNSType_CNAME)
+ {
+ domainname cname;
+ char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+ if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
+ LogMsg("queryrecord_result_callback: WCF CNAME putRData failed");
+ else
+ {
+ ConvertDomainNameToCString(&cname, cname_cstr);
+ CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+ {
+ WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
+ }
+ }
+ }
+ }
+ else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED");
+ }
+ }
#endif
#endif
- }
+ }
mDNSlocal void queryrecord_termination_callback(request_state *request)
- {
- LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP",
- request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype));
- if (request->u.queryrecord.q.QuestionContext)
- {
- mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check
- request->u.queryrecord.q.QuestionContext = mDNSNULL;
- }
- else
- {
- DNSQuestion *question = &request->u.queryrecord.q;
- LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
- }
-
- if (request->u.queryrecord.q.qnameOrig)
- {
- freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
- request->u.queryrecord.q.qnameOrig = mDNSNULL;
- }
- if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P)))
- {
- LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
- external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype);
- }
- if (request->u.queryrecord.q2)
- {
- if (request->u.queryrecord.q2->QuestionContext)
- {
- LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
- mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
- }
- else
- {
- DNSQuestion *question = request->u.queryrecord.q2;
- LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
- }
- if (request->u.queryrecord.q2->qnameOrig)
- {
- LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
- freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
- request->u.queryrecord.q2->qnameOrig = mDNSNULL;
- }
- freeL("queryrecord Q2", request->u.queryrecord.q2);
- request->u.queryrecord.q2 = mDNSNULL;
- }
- }
+ {
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP",
+ request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype));
+ if (request->u.queryrecord.q.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check
+ request->u.queryrecord.q.QuestionContext = mDNSNULL;
+ }
+ else
+ {
+ DNSQuestion *question = &request->u.queryrecord.q;
+ LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+
+ if (request->u.queryrecord.q.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
+ request->u.queryrecord.q.qnameOrig = mDNSNULL;
+ }
+ if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
+ external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype);
+ }
+ if (request->u.queryrecord.q2)
+ {
+ if (request->u.queryrecord.q2->QuestionContext)
+ {
+ LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+ }
+ else
+ {
+ DNSQuestion *question = request->u.queryrecord.q2;
+ LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+ if (request->u.queryrecord.q2->qnameOrig)
+ {
+ LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
+ freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
+ request->u.queryrecord.q2->qnameOrig = mDNSNULL;
+ }
+ freeL("queryrecord Q2", request->u.queryrecord.q2);
+ request->u.queryrecord.q2 = mDNSNULL;
+ }
+ }
mDNSlocal mStatus handle_queryrecord_request(request_state *request)
- {
- DNSQuestion *const q = &request->u.queryrecord.q;
- char name[256];
- mDNSu16 rrtype, rrclass;
- mStatus err;
-
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
-
- if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr);
- rrtype = get_uint16(&request->msgptr, request->msgend);
- rrclass = get_uint16(&request->msgptr, request->msgend);
-
- if (!request->msgptr)
- { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- request->flags = flags;
- mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
-
- q->InterfaceID = InterfaceID;
- q->Target = zeroAddr;
- if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr);
+ {
+ DNSQuestion *const q = &request->u.queryrecord.q;
+ char name[256];
+ mDNSu16 rrtype, rrclass;
+ mStatus err;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr);
+ rrtype = get_uint16(&request->msgptr, request->msgend);
+ rrclass = get_uint16(&request->msgptr, request->msgend);
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ request->flags = flags;
+ mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
+
+ q->InterfaceID = InterfaceID;
+ q->Target = zeroAddr;
+ if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr);
#if 0
- if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError);
+ if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError);
#endif
- q->qtype = rrtype;
- q->qclass = rrclass;
- q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
- q->ExpectUnique = mDNSfalse;
- q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
- q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
- q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
- q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
- q->WakeOnResolve = 0;
- q->QuestionCallback = queryrecord_result_callback;
- q->QuestionContext = request;
- q->SearchListIndex = 0;
-
- // Don't append search domains for fully qualified domain names including queries
- // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
- // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
- // append search domains or not. So, we record that information in AppendSearchDomains.
- //
- // We append search domains only for queries that are a single label. If overriden using
- // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
- // is not fully qualified.
-
- if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
- (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
- {
- q->AppendSearchDomains = 1;
- q->AppendLocalSearchDomains = 1;
- }
- else
- {
- q->AppendSearchDomains = 0;
- q->AppendLocalSearchDomains = 0;
- }
-
- // For single label queries that are not fully qualified, look at /etc/hosts, cache and try
- // search domains before trying them on the wire as a single label query. RetryWithSearchDomains
- // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
- // the cache
- q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
- q->qnameOrig = mDNSNULL;
-
- LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype));
- err = mDNS_StartQuery(&mDNSStorage, q);
- if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
- else
- {
- request->terminate = queryrecord_termination_callback;
- if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P)))
- {
- LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
- external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype);
- }
- }
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ q->ExpectUnique = mDNSfalse;
+ q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
+ q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ q->WakeOnResolve = 0;
+ q->QuestionCallback = queryrecord_result_callback;
+ q->QuestionContext = request;
+ q->SearchListIndex = 0;
+
+ // Don't append search domains for fully qualified domain names including queries
+ // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
+ // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
+ // append search domains or not. So, we record that information in AppendSearchDomains.
+ //
+ // We append search domains only for queries that are a single label. If overriden using
+ // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+ // is not fully qualified.
+
+ if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+ (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+ {
+ q->AppendSearchDomains = 1;
+ q->AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ q->AppendSearchDomains = 0;
+ q->AppendLocalSearchDomains = 0;
+ }
+
+ // For single label queries that are not fully qualified, look at /etc/hosts, cache and try
+ // search domains before trying them on the wire as a single label query. RetryWithSearchDomains
+ // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
+ // the cache
+ q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+ q->qnameOrig = mDNSNULL;
+
+ LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype));
+ err = mDNS_StartQuery(&mDNSStorage, q);
+ if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
+ else
+ {
+ request->terminate = queryrecord_termination_callback;
+ if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
+ external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype);
+ }
+ }
#if APPLE_OSX_mDNSResponder
- err = SendAdditionalQuery(q, request, err);
+ err = SendAdditionalQuery(q, request, err);
#endif // APPLE_OSX_mDNSResponder
- return(err);
- }
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -3028,113 +3030,113 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
#endif
mDNSlocal reply_state *format_enumeration_reply(request_state *request,
- const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err)
- {
- size_t len;
- reply_state *reply;
- char *data;
-
- len = sizeof(DNSServiceFlags);
- len += sizeof(mDNSu32);
- len += sizeof(DNSServiceErrorType);
- len += strlen(domain) + 1;
-
- reply = create_reply(enumeration_reply_op, len, request);
- reply->rhdr->flags = dnssd_htonl(flags);
- reply->rhdr->ifi = dnssd_htonl(ifi);
- reply->rhdr->error = dnssd_htonl(err);
- data = (char *)&reply->rhdr[1];
- put_string(domain, &data);
- return reply;
- }
+ const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err)
+ {
+ size_t len;
+ reply_state *reply;
+ char *data;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32);
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(domain) + 1;
+
+ reply = create_reply(enumeration_reply_op, len, request);
+ reply->rhdr->flags = dnssd_htonl(flags);
+ reply->rhdr->ifi = dnssd_htonl(ifi);
+ reply->rhdr->error = dnssd_htonl(err);
+ data = (char *)&reply->rhdr[1];
+ put_string(domain, &data);
+ return reply;
+ }
mDNSlocal void enum_termination_callback(request_state *request)
- {
- mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
- mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
- }
+ {
+ mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+ mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
+ }
mDNSlocal void enum_result_callback(mDNS *const m,
- DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord)
- {
- char domain[MAX_ESCAPED_DOMAIN_NAME];
- request_state *request = question->QuestionContext;
- DNSServiceFlags flags = 0;
- reply_state *reply;
- (void)m; // Unused
+ DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord)
+ {
+ char domain[MAX_ESCAPED_DOMAIN_NAME];
+ request_state *request = question->QuestionContext;
+ DNSServiceFlags flags = 0;
+ reply_state *reply;
+ (void)m; // Unused
- if (answer->rrtype != kDNSType_PTR) return;
+ if (answer->rrtype != kDNSType_PTR) return;
#if 0
- if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return;
+ if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return;
#endif
- // We only return add/remove events for the browse and registration lists
- // For the default browse and registration answers, we only give an "ADD" event
- if (question == &request->u.enumeration.q_default && !AddRecord) return;
+ // We only return add/remove events for the browse and registration lists
+ // For the default browse and registration answers, we only give an "ADD" event
+ if (question == &request->u.enumeration.q_default && !AddRecord) return;
- if (AddRecord)
- {
- flags |= kDNSServiceFlagsAdd;
- if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault;
- }
+ if (AddRecord)
+ {
+ flags |= kDNSServiceFlagsAdd;
+ if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault;
+ }
- ConvertDomainNameToCString(&answer->rdata->u.name, domain);
- // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
- // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
- // network, so we just pass kDNSServiceInterfaceIndexAny
- reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
- if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; }
+ ConvertDomainNameToCString(&answer->rdata->u.name, domain);
+ // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
+ // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
+ // network, so we just pass kDNSServiceInterfaceIndexAny
+ reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
+ if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; }
- LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain);
+ LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain);
- append_reply(request, reply);
- }
+ append_reply(request, reply);
+ }
mDNSlocal mStatus handle_enum_request(request_state *request)
- {
- mStatus err;
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains;
- mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
- mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
-
- if (!request->msgptr)
- { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- // allocate context structures
- uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
+ {
+ mStatus err;
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains;
+ mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+ mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // allocate context structures
+ uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
#if 0
- // mark which kind of enumeration we're doing so we can (de)authorize certain domains
- request->u.enumeration.flags = reg;
+ // mark which kind of enumeration we're doing so we can (de)authorize certain domains
+ request->u.enumeration.flags = reg;
#endif
- // enumeration requires multiple questions, so we must link all the context pointers so that
- // necessary context can be reached from the callbacks
- request->u.enumeration.q_all .QuestionContext = request;
- request->u.enumeration.q_default.QuestionContext = request;
-
- // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
- if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
-
- // make the calls
- LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags,
- (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
- (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
- err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request);
- if (!err)
- {
- err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request);
- if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
- else request->terminate = enum_termination_callback;
- }
-
- return(err);
- }
+ // enumeration requires multiple questions, so we must link all the context pointers so that
+ // necessary context can be reached from the callbacks
+ request->u.enumeration.q_all .QuestionContext = request;
+ request->u.enumeration.q_default.QuestionContext = request;
+
+ // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
+ if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
+
+ // make the calls
+ LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags,
+ (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
+ (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
+ err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request);
+ if (!err)
+ {
+ err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request);
+ if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+ else request->terminate = enum_termination_callback;
+ }
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -3143,62 +3145,62 @@ mDNSlocal mStatus handle_enum_request(request_state *request)
#endif
mDNSlocal mStatus handle_reconfirm_request(request_state *request)
- {
- mStatus status = mStatus_BadParamErr;
- AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0);
- if (rr)
- {
- status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
- LogOperation(
- (status == mStatus_NoError) ?
- "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
- "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
- request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
- mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
- freeL("AuthRecord/handle_reconfirm_request", rr);
- }
- return(status);
- }
+ {
+ mStatus status = mStatus_BadParamErr;
+ AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0);
+ if (rr)
+ {
+ status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
+ LogOperation(
+ (status == mStatus_NoError) ?
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
+ request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
+ mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
+ freeL("AuthRecord/handle_reconfirm_request", rr);
+ }
+ return(status);
+ }
mDNSlocal mStatus handle_setdomain_request(request_state *request)
- {
- char domainstr[MAX_ESCAPED_DOMAIN_NAME];
- domainname domain;
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- (void)flags; // Unused
- if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- !MakeDomainNameFromDNSNameString(&domain, domainstr))
- { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
- return(mStatus_NoError);
- }
+ {
+ char domainstr[MAX_ESCAPED_DOMAIN_NAME];
+ domainname domain;
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+ if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ !MakeDomainNameFromDNSNameString(&domain, domainstr))
+ { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
+ return(mStatus_NoError);
+ }
typedef packedstruct
- {
- mStatus err;
- mDNSu32 len;
- mDNSu32 vers;
- } DaemonVersionReply;
+ {
+ mStatus err;
+ mDNSu32 len;
+ mDNSu32 vers;
+ } DaemonVersionReply;
mDNSlocal void handle_getproperty_request(request_state *request)
- {
- const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr);
- char prop[256];
- if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
- {
- LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
- if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
- {
- DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
- send_all(request->sd, (const char *)&x, sizeof(x));
- return;
- }
- }
-
- // If we didn't recogize the requested property name, return BadParamErr
- send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
- }
+ {
+ const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr);
+ char prop[256];
+ if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
+ {
+ LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
+ if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
+ {
+ DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
+ send_all(request->sd, (const char *)&x, sizeof(x));
+ return;
+ }
+ }
+
+ // If we didn't recogize the requested property name, return BadParamErr
+ send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -3209,106 +3211,106 @@ mDNSlocal void handle_getproperty_request(request_state *request)
#define DNSServiceProtocol(X) ((X) == NATOp_AddrRequest ? 0 : (X) == NATOp_MapUDP ? kDNSServiceProtocol_UDP : kDNSServiceProtocol_TCP)
mDNSlocal void port_mapping_termination_callback(request_state *request)
- {
- LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP", request->sd,
- DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
- mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
- mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
- }
+ {
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP", request->sd,
+ DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+ mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
+ mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+ }
// Called via function pointer when we get a NAT-PMP address request or port mapping response
mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n)
- {
- request_state *request = (request_state *)n->clientContext;
- reply_state *rep;
- int replyLen;
- char *data;
-
- if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; }
-
- // calculate reply data length
- replyLen = sizeof(DNSServiceFlags);
- replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl
- replyLen += sizeof(DNSServiceErrorType);
- replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port
- replyLen += sizeof(mDNSu8); // protocol
-
- rep = create_reply(port_mapping_reply_op, replyLen, request);
-
- rep->rhdr->flags = dnssd_htonl(0);
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
- rep->rhdr->error = dnssd_htonl(n->Result);
-
- data = (char *)&rep->rhdr[1];
-
- *data++ = request->u.pm.NATinfo.ExternalAddress.b[0];
- *data++ = request->u.pm.NATinfo.ExternalAddress.b[1];
- *data++ = request->u.pm.NATinfo.ExternalAddress.b[2];
- *data++ = request->u.pm.NATinfo.ExternalAddress.b[3];
- *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol);
- *data++ = request->u.pm.NATinfo.IntPort.b[0];
- *data++ = request->u.pm.NATinfo.IntPort.b[1];
- *data++ = request->u.pm.NATinfo.ExternalPort.b[0];
- *data++ = request->u.pm.NATinfo.ExternalPort.b[1];
- put_uint32(request->u.pm.NATinfo.Lifetime, &data);
-
- LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd,
- DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
- mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
- &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime);
-
- append_reply(request, rep);
- }
+ {
+ request_state *request = (request_state *)n->clientContext;
+ reply_state *rep;
+ int replyLen;
+ char *data;
+
+ if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; }
+
+ // calculate reply data length
+ replyLen = sizeof(DNSServiceFlags);
+ replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl
+ replyLen += sizeof(DNSServiceErrorType);
+ replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port
+ replyLen += sizeof(mDNSu8); // protocol
+
+ rep = create_reply(port_mapping_reply_op, replyLen, request);
+
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
+ rep->rhdr->error = dnssd_htonl(n->Result);
+
+ data = (char *)&rep->rhdr[1];
+
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[0];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[1];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[2];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[3];
+ *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol);
+ *data++ = request->u.pm.NATinfo.IntPort.b[0];
+ *data++ = request->u.pm.NATinfo.IntPort.b[1];
+ *data++ = request->u.pm.NATinfo.ExternalPort.b[0];
+ *data++ = request->u.pm.NATinfo.ExternalPort.b[1];
+ put_uint32(request->u.pm.NATinfo.Lifetime, &data);
+
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd,
+ DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+ mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
+ &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime);
+
+ append_reply(request, rep);
+ }
mDNSlocal mStatus handle_port_mapping_request(request_state *request)
- {
- mDNSu32 ttl = 0;
- mStatus err = mStatus_NoError;
-
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend);
- (void)flags; // Unused
- if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
- if (request->msgptr + 8 > request->msgend) request->msgptr = NULL;
- else
- {
- request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++;
- request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++;
- request->u.pm.ReqExt.b[0] = *request->msgptr++;
- request->u.pm.ReqExt.b[1] = *request->msgptr++;
- ttl = get_uint32(&request->msgptr, request->msgend);
- }
-
- if (!request->msgptr)
- { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
-
- if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too
- {
- if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr);
- }
- else
- {
- if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr);
- if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr);
- }
-
- request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP;
- // u.pm.NATinfo.IntPort = already set above
- request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt;
- request->u.pm.NATinfo.NATLease = ttl;
- request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback;
- request->u.pm.NATinfo.clientContext = request;
-
- LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START", request->sd,
- protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
- err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
- if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
- else request->terminate = port_mapping_termination_callback;
-
- return(err);
- }
+ {
+ mDNSu32 ttl = 0;
+ mStatus err = mStatus_NoError;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+ if (request->msgptr + 8 > request->msgend) request->msgptr = NULL;
+ else
+ {
+ request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++;
+ request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++;
+ request->u.pm.ReqExt.b[0] = *request->msgptr++;
+ request->u.pm.ReqExt.b[1] = *request->msgptr++;
+ ttl = get_uint32(&request->msgptr, request->msgend);
+ }
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too
+ {
+ if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr);
+ }
+ else
+ {
+ if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr);
+ if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr);
+ }
+
+ request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP;
+ // u.pm.NATinfo.IntPort = already set above
+ request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt;
+ request->u.pm.NATinfo.NATLease = ttl;
+ request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback;
+ request->u.pm.NATinfo.clientContext = request;
+
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START", request->sd,
+ protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
+ err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+ if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
+ else request->terminate = port_mapping_termination_callback;
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -3317,184 +3319,184 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request)
#endif
mDNSlocal void addrinfo_termination_callback(request_state *request)
- {
- LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c);
-
- if (request->u.addrinfo.q4.QuestionContext)
- {
- mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
- request->u.addrinfo.q4.QuestionContext = mDNSNULL;
- }
- if (request->u.addrinfo.q4.qnameOrig)
- {
- freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
- request->u.addrinfo.q4.qnameOrig = mDNSNULL;
- }
- if (request->u.addrinfo.q42)
- {
- if (request->u.addrinfo.q42->QuestionContext)
- {
- LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
- mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
- }
- if (request->u.addrinfo.q42->qnameOrig)
- {
- LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
- freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
- request->u.addrinfo.q42->qnameOrig = mDNSNULL;
- }
- freeL("addrinfo Q42", request->u.addrinfo.q42);
- request->u.addrinfo.q42 = mDNSNULL;
- }
-
- if (request->u.addrinfo.q6.QuestionContext)
- {
- mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
- request->u.addrinfo.q6.QuestionContext = mDNSNULL;
- }
- if (request->u.addrinfo.q6.qnameOrig)
- {
- freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
- request->u.addrinfo.q6.qnameOrig = mDNSNULL;
- }
- if (request->u.addrinfo.q62)
- {
- if (request->u.addrinfo.q62->QuestionContext)
- {
- LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
- mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
- }
- if (request->u.addrinfo.q62->qnameOrig)
- {
- LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
- freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
- request->u.addrinfo.q62->qnameOrig = mDNSNULL;
- }
- freeL("addrinfo Q62", request->u.addrinfo.q62);
- request->u.addrinfo.q62 = mDNSNULL;
- }
- }
+ {
+ LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c);
+
+ if (request->u.addrinfo.q4.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+ request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+ }
+ if (request->u.addrinfo.q4.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
+ request->u.addrinfo.q4.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q42)
+ {
+ if (request->u.addrinfo.q42->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+ }
+ if (request->u.addrinfo.q42->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
+ freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
+ request->u.addrinfo.q42->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q42", request->u.addrinfo.q42);
+ request->u.addrinfo.q42 = mDNSNULL;
+ }
+
+ if (request->u.addrinfo.q6.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+ request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+ }
+ if (request->u.addrinfo.q6.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
+ request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q62)
+ {
+ if (request->u.addrinfo.q62->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+ }
+ if (request->u.addrinfo.q62->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
+ freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
+ request->u.addrinfo.q62->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q62", request->u.addrinfo.q62);
+ request->u.addrinfo.q62 = mDNSNULL;
+ }
+ }
mDNSlocal mStatus handle_addrinfo_request(request_state *request)
- {
- char hostname[256];
- domainname d;
- mStatus err = 0;
+ {
+ char hostname[256];
+ domainname d;
+ mStatus err = 0;
- DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
- mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
- mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo));
- request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- request->u.addrinfo.flags = flags;
- request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend);
+ mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo));
+ request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ request->u.addrinfo.flags = flags;
+ request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend);
- if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr);
- if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr);
+ if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr);
+ if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr);
- if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr);
+ if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr);
- if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
- if (!MakeDomainNameFromDNSNameString(&d, hostname))
- { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); }
+ if (!MakeDomainNameFromDNSNameString(&d, hostname))
+ { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); }
#if 0
- if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError);
+ if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError);
#endif
- if (!request->u.addrinfo.protocol)
- {
- flags |= kDNSServiceFlagsSuppressUnusable;
- request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
- }
-
- request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id;
- request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr;
- request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d;
- request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN;
- request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
- request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse;
- request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
- request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
- request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
- request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
- request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0;
- request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL;
-
- if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
- {
- request->u.addrinfo.q4.qtype = kDNSServiceType_A;
- request->u.addrinfo.q4.SearchListIndex = 0;
-
- // We append search domains only for queries that are a single label. If overriden using
- // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
- // is not fully qualified.
- if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
- {
- request->u.addrinfo.q4.AppendSearchDomains = 1;
- request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
- }
- else
- {
- request->u.addrinfo.q4.AppendSearchDomains = 0;
- request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
- }
- request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
- request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
- request->u.addrinfo.q4.QuestionContext = request;
- err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
- if (err != mStatus_NoError)
- {
- LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
- request->u.addrinfo.q4.QuestionContext = mDNSNULL;
- }
- #if APPLE_OSX_mDNSResponder
- err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
- #endif // APPLE_OSX_mDNSResponder
- }
-
- if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
- {
- request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA;
- request->u.addrinfo.q6.SearchListIndex = 0;
- if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
- {
- request->u.addrinfo.q6.AppendSearchDomains = 1;
- request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
- }
- else
- {
- request->u.addrinfo.q6.AppendSearchDomains = 0;
- request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
- }
- request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
- request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
- request->u.addrinfo.q6.QuestionContext = request;
- err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
- if (err != mStatus_NoError)
- {
- LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
- request->u.addrinfo.q6.QuestionContext = mDNSNULL;
- if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
- {
- // If we started a query for IPv4, we need to cancel it
- mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
- request->u.addrinfo.q4.QuestionContext = mDNSNULL;
- }
- }
- #if APPLE_OSX_mDNSResponder
- err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
- #endif // APPLE_OSX_mDNSResponder
- }
-
- LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START",
- request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c);
-
- if (!err) request->terminate = addrinfo_termination_callback;
-
- return(err);
- }
+ if (!request->u.addrinfo.protocol)
+ {
+ flags |= kDNSServiceFlagsSuppressUnusable;
+ request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
+ }
+
+ request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id;
+ request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr;
+ request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d;
+ request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN;
+ request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse;
+ request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
+ request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0;
+ request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+ {
+ request->u.addrinfo.q4.qtype = kDNSServiceType_A;
+ request->u.addrinfo.q4.SearchListIndex = 0;
+
+ // We append search domains only for queries that are a single label. If overriden using
+ // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+ // is not fully qualified.
+ if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 1;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 0;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
+ request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
+ request->u.addrinfo.q4.QuestionContext = request;
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
+ if (err != mStatus_NoError)
+ {
+ LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+ request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+ }
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+ #endif // APPLE_OSX_mDNSResponder
+ }
+
+ if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
+ {
+ request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA;
+ request->u.addrinfo.q6.SearchListIndex = 0;
+ if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 1;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 0;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
+ request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
+ request->u.addrinfo.q6.QuestionContext = request;
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
+ if (err != mStatus_NoError)
+ {
+ LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+ request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+ {
+ // If we started a query for IPv4, we need to cancel it
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+ request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+ }
+ }
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
+ #endif // APPLE_OSX_mDNSResponder
+ }
+
+ LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START",
+ request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c);
+
+ if (!err) request->terminate = addrinfo_termination_callback;
+
+ return(err);
+ }
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@@ -3503,1214 +3505,1214 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
#endif
mDNSlocal request_state *NewRequest(void)
- {
- request_state **p = &all_requests;
- while (*p) p=&(*p)->next;
- *p = mallocL("request_state", sizeof(request_state));
- if (!*p) FatalError("ERROR: malloc");
- mDNSPlatformMemZero(*p, sizeof(request_state));
- return(*p);
- }
+ {
+ request_state **p = &all_requests;
+ while (*p) p=&(*p)->next;
+ *p = mallocL("request_state", sizeof(request_state));
+ if (!*p) FatalError("ERROR: malloc");
+ mDNSPlatformMemZero(*p, sizeof(request_state));
+ return(*p);
+ }
// read_msg may be called any time when the transfer state (req->ts) is t_morecoming.
// if there is no data on the socket, the socket will be closed and t_terminated will be returned
mDNSlocal void read_msg(request_state *req)
- {
- if (req->ts == t_terminated || req->ts == t_error)
- { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
-
- if (req->ts == t_complete) // this must be death or something is wrong
- {
- char buf[4]; // dummy for death notification
- int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data);
- if (!nread) { req->ts = t_terminated; return; }
- if (nread < 0) goto rerror;
- LogMsg("%3d: ERROR: read data from a completed request", req->sd);
- req->ts = t_error;
- return;
- }
-
- if (req->ts != t_morecoming)
- { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
-
- if (req->hdr_bytes < sizeof(ipc_msg_hdr))
- {
- mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
- int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data);
- if (nread == 0) { req->ts = t_terminated; return; }
- if (nread < 0) goto rerror;
- req->hdr_bytes += nread;
- if (req->hdr_bytes > sizeof(ipc_msg_hdr))
- { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
-
- // only read data if header is complete
- if (req->hdr_bytes == sizeof(ipc_msg_hdr))
- {
- ConvertHeaderBytes(&req->hdr);
- if (req->hdr.version != VERSION)
- { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
-
- // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
- // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin
- // for other overhead, this means any message above 70kB is definitely bogus.
- if (req->hdr.datalen > 70000)
- { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
- req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
- if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
- req->msgptr = req->msgbuf;
- req->msgend = req->msgbuf + req->hdr.datalen;
- mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
- }
- }
-
- // If our header is complete, but we're still needing more body data, then try to read it now
- // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request
- // Any time we need to get the error return socket we know we'll have at least one data byte
- // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
- if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
- {
- mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
- int nread;
+ {
+ if (req->ts == t_terminated || req->ts == t_error)
+ { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
+
+ if (req->ts == t_complete) // this must be death or something is wrong
+ {
+ char buf[4]; // dummy for death notification
+ int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data);
+ if (!nread) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ LogMsg("%3d: ERROR: read data from a completed request", req->sd);
+ req->ts = t_error;
+ return;
+ }
+
+ if (req->ts != t_morecoming)
+ { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
+
+ if (req->hdr_bytes < sizeof(ipc_msg_hdr))
+ {
+ mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
+ int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data);
+ if (nread == 0) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ req->hdr_bytes += nread;
+ if (req->hdr_bytes > sizeof(ipc_msg_hdr))
+ { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
+
+ // only read data if header is complete
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ ConvertHeaderBytes(&req->hdr);
+ if (req->hdr.version != VERSION)
+ { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
+
+ // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
+ // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin
+ // for other overhead, this means any message above 70kB is definitely bogus.
+ if (req->hdr.datalen > 70000)
+ { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
+ req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
+ if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
+ req->msgptr = req->msgbuf;
+ req->msgend = req->msgbuf + req->hdr.datalen;
+ mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
+ }
+ }
+
+ // If our header is complete, but we're still needing more body data, then try to read it now
+ // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request
+ // Any time we need to get the error return socket we know we'll have at least one data byte
+ // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
+ {
+ mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
+ int nread;
#if !defined(_WIN32)
- struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put
- struct msghdr msg;
- struct cmsghdr *cmsg;
- char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_iov = &vec;
- msg.msg_iovlen = 1;
- msg.msg_control = cbuf;
- msg.msg_controllen = sizeof(cbuf);
- msg.msg_flags = 0;
- nread = recvmsg(req->sd, &msg, 0);
+ struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ msg.msg_flags = 0;
+ nread = recvmsg(req->sd, &msg, 0);
#else
- nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data);
+ nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data);
#endif
- if (nread == 0) { req->ts = t_terminated; return; }
- if (nread < 0) goto rerror;
- req->data_bytes += nread;
- if (req->data_bytes > req->hdr.datalen)
- { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
+ if (nread == 0) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ req->data_bytes += nread;
+ if (req->data_bytes > req->hdr.datalen)
+ { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
#if !defined(_WIN32)
- cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg = CMSG_FIRSTHDR(&msg);
#if DEBUG_64BIT_SCM_RIGHTS
- LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS);
- LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
+ LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS);
+ LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
#endif // DEBUG_64BIT_SCM_RIGHTS
- if (msg.msg_controllen == sizeof(cbuf) &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) &&
- cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS)
- {
+ if (msg.msg_controllen == sizeof(cbuf) &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS)
+ {
#if APPLE_OSX_mDNSResponder
- // Strictly speaking BPF_fd belongs solely in the platform support layer, but because
- // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper,
- // and it's convenient to repurpose the existing fd-passing code here for that task
- if (req->hdr.op == send_bpf)
- {
- dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
- LogOperation("%3d: Got BPF %d", req->sd, x);
- mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
- }
- else
+ // Strictly speaking BPF_fd belongs solely in the platform support layer, but because
+ // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper,
+ // and it's convenient to repurpose the existing fd-passing code here for that task
+ if (req->hdr.op == send_bpf)
+ {
+ dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+ LogOperation("%3d: Got BPF %d", req->sd, x);
+ mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
+ }
+ else
#endif // APPLE_OSX_mDNSResponder
- req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+ req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
#if DEBUG_64BIT_SCM_RIGHTS
- LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
+ LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
#endif // DEBUG_64BIT_SCM_RIGHTS
- if (req->data_bytes < req->hdr.datalen)
- {
- LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
- req->sd, req->errsd, req->data_bytes, req->hdr.datalen);
- req->ts = t_error;
- return;
- }
- }
+ if (req->data_bytes < req->hdr.datalen)
+ {
+ LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
+ req->sd, req->errsd, req->data_bytes, req->hdr.datalen);
+ req->ts = t_error;
+ return;
+ }
+ }
#endif
- }
-
- // If our header and data are both complete, see if we need to make our separate error return socket
- if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen)
- {
- if (req->terminate && req->hdr.op != cancel_request)
- {
- dnssd_sockaddr_t cliaddr;
+ }
+
+ // If our header and data are both complete, see if we need to make our separate error return socket
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen)
+ {
+ if (req->terminate && req->hdr.op != cancel_request)
+ {
+ dnssd_sockaddr_t cliaddr;
#if defined(USE_TCP_LOOPBACK)
- mDNSOpaque16 port;
- u_long opt = 1;
- port.b[0] = req->msgptr[0];
- port.b[1] = req->msgptr[1];
- req->msgptr += 2;
- cliaddr.sin_family = AF_INET;
- cliaddr.sin_port = port.NotAnInteger;
- cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ mDNSOpaque16 port;
+ u_long opt = 1;
+ port.b[0] = req->msgptr[0];
+ port.b[1] = req->msgptr[1];
+ req->msgptr += 2;
+ cliaddr.sin_family = AF_INET;
+ cliaddr.sin_port = port.NotAnInteger;
+ cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
#else
- char ctrl_path[MAX_CTLPATH];
- get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer
- mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr));
- cliaddr.sun_family = AF_LOCAL;
- mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path);
- // If the error return path UDS name is empty string, that tells us
- // that this is a new version of the library that's going to pass us
- // the error return path socket via sendmsg/recvmsg
- if (ctrl_path[0] == 0)
- {
- if (req->errsd == req->sd)
- { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
- goto got_errfd;
- }
+ char ctrl_path[MAX_CTLPATH];
+ get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer
+ mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr));
+ cliaddr.sun_family = AF_LOCAL;
+ mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path);
+ // If the error return path UDS name is empty string, that tells us
+ // that this is a new version of the library that's going to pass us
+ // the error return path socket via sendmsg/recvmsg
+ if (ctrl_path[0] == 0)
+ {
+ if (req->errsd == req->sd)
+ { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
+ goto got_errfd;
+ }
#endif
-
- req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
- if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; }
- if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
- {
+ req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; }
+
+ if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
#if !defined(USE_TCP_LOOPBACK)
- struct stat sb;
+ struct stat sb;
LogMsg("%3d: read_msg: Couldn't connect to error return path socket '%s' errno %d (%s)",
- req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
- if (stat(cliaddr.sun_path, &sb) < 0)
+ req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
+ if (stat(cliaddr.sun_path, &sb) < 0)
LogMsg("%3d: read_msg: stat failed '%s' errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
- else
+ else
LogMsg("%3d: read_msg: file '%s' mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
#endif
- req->ts = t_error;
- return;
- }
-
+ req->ts = t_error;
+ return;
+ }
+
#if !defined(USE_TCP_LOOPBACK)
got_errfd:
#endif
- LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]);
+ LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]);
#if defined(_WIN32)
- if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0)
+ if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0)
#else
- if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
+ if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
#endif
- {
- LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)",
- req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
- req->ts = t_error;
- return;
- }
- }
-
- req->ts = t_complete;
- }
-
- return;
+ {
+ LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)",
+ req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ req->ts = t_error;
+ return;
+ }
+ }
+
+ req->ts = t_complete;
+ }
+
+ return;
rerror:
- if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return;
- LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
- req->ts = t_error;
- }
+ if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return;
+ LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ req->ts = t_error;
+ }
#define RecordOrientedOp(X) \
- ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request)
+ ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request)
// The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them
#define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request)
mDNSlocal void request_callback(int fd, short filter, void *info)
- {
- mStatus err = 0;
- request_state *req = info;
- mDNSs32 min_size = sizeof(DNSServiceFlags);
- (void)fd; // Unused
- (void)filter; // Unused
-
- for (;;)
- {
- read_msg(req);
- if (req->ts == t_morecoming) return;
- if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
- if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
-
- if (req->hdr.version != VERSION)
- {
- LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
- AbortUnlinkAndFree(req);
- return;
- }
-
- switch(req->hdr.op) // Interface + other data
- {
- case connection_request: min_size = 0; break;
- case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
- case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break;
- case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break;
- case remove_record_request: break;
- case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break;
- case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
- case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break;
- case enumeration_request: min_size += sizeof(mDNSu32); break;
- case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break;
- case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break;
- case setdomain_request: min_size += 1 /* domain */; break;
- case getproperty_request: min_size = 2; break;
- case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break;
- case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break;
- case send_bpf: // Same as cancel_request below
- case cancel_request: min_size = 0; break;
- default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break;
- }
-
- if ((mDNSs32)req->data_bytes < min_size)
- { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
-
- if (LightweightOp(req->hdr.op) && !req->terminate)
- { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; }
-
- // check if client wants silent operation
- if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
-
- // If req->terminate is already set, this means this operation is sharing an existing connection
- if (req->terminate && !LightweightOp(req->hdr.op))
- {
- request_state *newreq = NewRequest();
- newreq->primary = req;
- newreq->sd = req->sd;
- newreq->errsd = req->errsd;
- newreq->uid = req->uid;
- newreq->hdr = req->hdr;
- newreq->msgbuf = req->msgbuf;
- newreq->msgptr = req->msgptr;
- newreq->msgend = req->msgend;
- req = newreq;
- }
-
- // If we're shutting down, don't allow new client requests
- // We do allow "cancel" and "getproperty" during shutdown
- if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
- {
- err = mStatus_ServiceNotRunning;
- }
- else switch(req->hdr.op)
- {
- // These are all operations that have their own first-class request_state object
- case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
- req->terminate = connection_termination; break;
- case resolve_request: err = handle_resolve_request (req); break;
- case query_request: err = handle_queryrecord_request (req); break;
- case browse_request: err = handle_browse_request (req); break;
- case reg_service_request: err = handle_regservice_request (req); break;
- case enumeration_request: err = handle_enum_request (req); break;
- case reconfirm_record_request: err = handle_reconfirm_request (req); break;
- case setdomain_request: err = handle_setdomain_request (req); break;
- case getproperty_request: handle_getproperty_request (req); break;
- case port_mapping_request: err = handle_port_mapping_request(req); break;
- case addrinfo_request: err = handle_addrinfo_request (req); break;
- case send_bpf: /* Do nothing for send_bpf */ break;
-
- // These are all operations that work with an existing request_state object
- case reg_record_request: err = handle_regrecord_request (req); break;
- case add_record_request: err = handle_add_request (req); break;
- case update_record_request: err = handle_update_request (req); break;
- case remove_record_request: err = handle_removerecord_request(req); break;
- case cancel_request: handle_cancel_request (req); break;
- default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
- }
-
- // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
- if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
-
- // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
- // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
- if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
- {
- const mStatus err_netorder = dnssd_htonl(err);
- send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
- if (req->errsd != req->sd)
- {
- LogOperation("%3d: Error socket %d closed %08X %08X (%d)",
- req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
- dnssd_close(req->errsd);
- req->errsd = req->sd;
- // Also need to reset the parent's errsd, if this is a subordinate operation
- if (req->primary) req->primary->errsd = req->primary->sd;
- }
- }
-
- // Reset ready to accept the next req on this pipe
- if (req->primary) req = req->primary;
- req->ts = t_morecoming;
- req->hdr_bytes = 0;
- req->data_bytes = 0;
- req->msgbuf = mDNSNULL;
- req->msgptr = mDNSNULL;
- req->msgend = 0;
- }
- }
+ {
+ mStatus err = 0;
+ request_state *req = info;
+ mDNSs32 min_size = sizeof(DNSServiceFlags);
+ (void)fd; // Unused
+ (void)filter; // Unused
+
+ for (;;)
+ {
+ read_msg(req);
+ if (req->ts == t_morecoming) return;
+ if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
+ if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
+
+ if (req->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+
+ switch(req->hdr.op) // Interface + other data
+ {
+ case connection_request: min_size = 0; break;
+ case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
+ case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break;
+ case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break;
+ case remove_record_request: break;
+ case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break;
+ case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
+ case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break;
+ case enumeration_request: min_size += sizeof(mDNSu32); break;
+ case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break;
+ case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break;
+ case setdomain_request: min_size += 1 /* domain */; break;
+ case getproperty_request: min_size = 2; break;
+ case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break;
+ case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break;
+ case send_bpf: // Same as cancel_request below
+ case cancel_request: min_size = 0; break;
+ default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break;
+ }
+
+ if ((mDNSs32)req->data_bytes < min_size)
+ { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
+
+ if (LightweightOp(req->hdr.op) && !req->terminate)
+ { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; }
+
+ // check if client wants silent operation
+ if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
+
+ // If req->terminate is already set, this means this operation is sharing an existing connection
+ if (req->terminate && !LightweightOp(req->hdr.op))
+ {
+ request_state *newreq = NewRequest();
+ newreq->primary = req;
+ newreq->sd = req->sd;
+ newreq->errsd = req->errsd;
+ newreq->uid = req->uid;
+ newreq->hdr = req->hdr;
+ newreq->msgbuf = req->msgbuf;
+ newreq->msgptr = req->msgptr;
+ newreq->msgend = req->msgend;
+ req = newreq;
+ }
+
+ // If we're shutting down, don't allow new client requests
+ // We do allow "cancel" and "getproperty" during shutdown
+ if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
+ {
+ err = mStatus_ServiceNotRunning;
+ }
+ else switch(req->hdr.op)
+ {
+ // These are all operations that have their own first-class request_state object
+ case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
+ req->terminate = connection_termination; break;
+ case resolve_request: err = handle_resolve_request (req); break;
+ case query_request: err = handle_queryrecord_request (req); break;
+ case browse_request: err = handle_browse_request (req); break;
+ case reg_service_request: err = handle_regservice_request (req); break;
+ case enumeration_request: err = handle_enum_request (req); break;
+ case reconfirm_record_request: err = handle_reconfirm_request (req); break;
+ case setdomain_request: err = handle_setdomain_request (req); break;
+ case getproperty_request: handle_getproperty_request (req); break;
+ case port_mapping_request: err = handle_port_mapping_request(req); break;
+ case addrinfo_request: err = handle_addrinfo_request (req); break;
+ case send_bpf: /* Do nothing for send_bpf */ break;
+
+ // These are all operations that work with an existing request_state object
+ case reg_record_request: err = handle_regrecord_request (req); break;
+ case add_record_request: err = handle_add_request (req); break;
+ case update_record_request: err = handle_update_request (req); break;
+ case remove_record_request: err = handle_removerecord_request(req); break;
+ case cancel_request: handle_cancel_request (req); break;
+ default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
+ }
+
+ // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
+ if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
+
+ // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
+ // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
+ if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
+ {
+ const mStatus err_netorder = dnssd_htonl(err);
+ send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
+ if (req->errsd != req->sd)
+ {
+ LogOperation("%3d: Error socket %d closed %08X %08X (%d)",
+ req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
+ dnssd_close(req->errsd);
+ req->errsd = req->sd;
+ // Also need to reset the parent's errsd, if this is a subordinate operation
+ if (req->primary) req->primary->errsd = req->primary->sd;
+ }
+ }
+
+ // Reset ready to accept the next req on this pipe
+ if (req->primary) req = req->primary;
+ req->ts = t_morecoming;
+ req->hdr_bytes = 0;
+ req->data_bytes = 0;
+ req->msgbuf = mDNSNULL;
+ req->msgptr = mDNSNULL;
+ req->msgend = 0;
+ }
+ }
mDNSlocal void connect_callback(int fd, short filter, void *info)
- {
- dnssd_sockaddr_t cliaddr;
- dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr);
- dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len);
+ {
+ dnssd_sockaddr_t cliaddr;
+ dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr);
+ dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len);
#if defined(SO_NOSIGPIPE) || defined(_WIN32)
- unsigned long optval = 1;
+ unsigned long optval = 1;
#endif
- (void)filter; // Unused
- (void)info; // Unused
+ (void)filter; // Unused
+ (void)info; // Unused
- if (!dnssd_SocketValid(sd))
- {
- if (dnssd_errno != dnssd_EWOULDBLOCK) my_perror("ERROR: accept");
- return;
- }
+ if (!dnssd_SocketValid(sd))
+ {
+ if (dnssd_errno != dnssd_EWOULDBLOCK) my_perror("ERROR: accept");
+ return;
+ }
#ifdef SO_NOSIGPIPE
- // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
- if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
- LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
+ if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+ LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno));
#endif
#if defined(_WIN32)
- if (ioctlsocket(sd, FIONBIO, &optval) != 0)
+ if (ioctlsocket(sd, FIONBIO, &optval) != 0)
#else
- if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0)
+ if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0)
#endif
- {
- my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
- dnssd_close(sd);
- return;
- }
- else
- {
- request_state *request = NewRequest();
- request->ts = t_morecoming;
- request->sd = sd;
- request->errsd = sd;
+ {
+ my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
+ dnssd_close(sd);
+ return;
+ }
+ else
+ {
+ request_state *request = NewRequest();
+ request->ts = t_morecoming;
+ request->sd = sd;
+ request->errsd = sd;
#if APPLE_OSX_mDNSResponder
- struct xucred x;
- socklen_t xucredlen = sizeof(x);
- if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid;
- else my_perror("ERROR: getsockopt, LOCAL_PEERCRED");
- debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups);
+ struct xucred x;
+ socklen_t xucredlen = sizeof(x);
+ if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid;
+ else my_perror("ERROR: getsockopt, LOCAL_PEERCRED");
+ debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups);
#endif // APPLE_OSX_mDNSResponder
- LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid);
- udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data);
- }
- }
+ LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid);
+ udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data);
+ }
+ }
mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt)
- {
+ {
#if defined(SO_NP_EXTENSIONS)
- struct so_np_extensions sonpx;
- socklen_t optlen = sizeof(struct so_np_extensions);
- sonpx.npx_flags = SONPX_SETOPTSHUT;
- sonpx.npx_mask = SONPX_SETOPTSHUT;
- if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0)
- my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS");
+ struct so_np_extensions sonpx;
+ socklen_t optlen = sizeof(struct so_np_extensions);
+ sonpx.npx_flags = SONPX_SETOPTSHUT;
+ sonpx.npx_mask = SONPX_SETOPTSHUT;
+ if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0)
+ my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS");
#endif
#if defined(_WIN32)
- // SEH: do we even need to do this on windows?
- // This socket will be given to WSAEventSelect which will automatically set it to non-blocking
- u_long opt = 1;
- if (ioctlsocket(skt, FIONBIO, &opt) != 0)
+ // SEH: do we even need to do this on windows?
+ // This socket will be given to WSAEventSelect which will automatically set it to non-blocking
+ u_long opt = 1;
+ if (ioctlsocket(skt, FIONBIO, &opt) != 0)
#else
- if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0)
+ if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0)
#endif
- {
- my_perror("ERROR: could not set listen socket to non-blocking mode");
- return mDNSfalse;
- }
-
- if (listen(skt, LISTENQ) != 0)
- {
- my_perror("ERROR: could not listen on listen socket");
- return mDNSfalse;
- }
-
- if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL))
- {
- my_perror("ERROR: could not add listen socket to event loop");
- return mDNSfalse;
- }
- else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt);
-
- return mDNStrue;
- }
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ return mDNSfalse;
+ }
+
+ if (listen(skt, LISTENQ) != 0)
+ {
+ my_perror("ERROR: could not listen on listen socket");
+ return mDNSfalse;
+ }
+
+ if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL))
+ {
+ my_perror("ERROR: could not add listen socket to event loop");
+ return mDNSfalse;
+ }
+ else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt);
+
+ return mDNStrue;
+ }
mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count)
- {
- dnssd_sockaddr_t laddr;
- int ret;
- mDNSu32 i = 0;
-
- LogInfo("udsserver_init");
-
- // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
- if (PID_FILE[0])
- {
- FILE *fp = fopen(PID_FILE, "w");
- if (fp != NULL)
- {
- fprintf(fp, "%d\n", getpid());
- fclose(fp);
- }
- }
-
- if (skts)
- {
- for (i = 0; i < count; i++)
- if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i]))
- goto error;
- }
- else
- {
- listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
- if (!dnssd_SocketValid(listenfd))
- {
- my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
- goto error;
- }
-
- mDNSPlatformMemZero(&laddr, sizeof(laddr));
-
- #if defined(USE_TCP_LOOPBACK)
- {
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
- laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
- ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
- if (ret < 0)
- {
- my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
- goto error;
- }
- }
- #else
- {
- mode_t mask = umask(0);
- unlink(MDNS_UDS_SERVERPATH); // OK if this fails
- laddr.sun_family = AF_LOCAL;
- #ifndef NOT_HAVE_SA_LEN
- // According to Stevens (section 3.2), there is no portable way to
- // determine whether sa_len is defined on a particular platform.
- laddr.sun_len = sizeof(struct sockaddr_un);
- #endif
- mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH);
- ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
- umask(mask);
- if (ret < 0)
- {
- my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
- goto error;
- }
- }
- #endif
-
- if (!uds_socket_setup(listenfd)) goto error;
- }
+ {
+ dnssd_sockaddr_t laddr;
+ int ret;
+ mDNSu32 i = 0;
+
+ LogInfo("udsserver_init");
+
+ // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
+ if (PID_FILE[0])
+ {
+ FILE *fp = fopen(PID_FILE, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ }
+
+ if (skts)
+ {
+ for (i = 0; i < count; i++)
+ if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i]))
+ goto error;
+ }
+ else
+ {
+ listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(listenfd))
+ {
+ my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
+ goto error;
+ }
+
+ mDNSPlatformMemZero(&laddr, sizeof(laddr));
+
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+ laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #else
+ {
+ mode_t mask = umask(0);
+ unlink(MDNS_UDS_SERVERPATH); // OK if this fails
+ laddr.sun_family = AF_LOCAL;
+ #ifndef NOT_HAVE_SA_LEN
+ // According to Stevens (section 3.2), there is no portable way to
+ // determine whether sa_len is defined on a particular platform.
+ laddr.sun_len = sizeof(struct sockaddr_un);
+ #endif
+ mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ umask(mask);
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #endif
+
+ if (!uds_socket_setup(listenfd)) goto error;
+ }
#if !defined(PLATFORM_NO_RLIMIT)
- {
- // Set maximum number of open file descriptors
- #define MIN_OPENFILES 10240
- struct rlimit maxfds, newfds;
-
- // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
- // you have to get and set rlimits once before getrlimit will return sensible values
- if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
- if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
-
- if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
- newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
- newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
- if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
- if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
-
- if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
- debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
- debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
- }
+ {
+ // Set maximum number of open file descriptors
+ #define MIN_OPENFILES 10240
+ struct rlimit maxfds, newfds;
+
+ // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
+ // you have to get and set rlimits once before getrlimit will return sensible values
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
+ newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
+ if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
+ if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
+ debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
+ }
#endif
- // We start a "LocalOnly" query looking for Automatic Browse Domain records.
- // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine
- // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked
- mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic,
- mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL);
+ // We start a "LocalOnly" query looking for Automatic Browse Domain records.
+ // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine
+ // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked
+ mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic,
+ mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL);
- // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain
- RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration);
- RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse);
- AddAutoBrowseDomain(0, &localdomain);
+ // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain
+ RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration);
+ RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse);
+ AddAutoBrowseDomain(0, &localdomain);
- udsserver_handle_configchange(&mDNSStorage);
- return 0;
+ udsserver_handle_configchange(&mDNSStorage);
+ return 0;
error:
- my_perror("ERROR: udsserver_init");
- return -1;
- }
+ my_perror("ERROR: udsserver_init");
+ return -1;
+ }
mDNSexport int udsserver_exit(void)
- {
- // Cancel all outstanding client requests
- while (all_requests) AbortUnlinkAndFree(all_requests);
-
- // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we
- // created in udsserver_init, and others we created as a result of reading local configuration data
- while (LocalDomainEnumRecords)
- {
- ARListElem *rem = LocalDomainEnumRecords;
- LocalDomainEnumRecords = LocalDomainEnumRecords->next;
- mDNS_Deregister(&mDNSStorage, &rem->ar);
- }
-
- // If the launching environment created no listening socket,
- // that means we created it ourselves, so we should clean it up on exit
- if (dnssd_SocketValid(listenfd))
- {
- dnssd_close(listenfd);
+ {
+ // Cancel all outstanding client requests
+ while (all_requests) AbortUnlinkAndFree(all_requests);
+
+ // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we
+ // created in udsserver_init, and others we created as a result of reading local configuration data
+ while (LocalDomainEnumRecords)
+ {
+ ARListElem *rem = LocalDomainEnumRecords;
+ LocalDomainEnumRecords = LocalDomainEnumRecords->next;
+ mDNS_Deregister(&mDNSStorage, &rem->ar);
+ }
+
+ // If the launching environment created no listening socket,
+ // that means we created it ourselves, so we should clean it up on exit
+ if (dnssd_SocketValid(listenfd))
+ {
+ dnssd_close(listenfd);
#if !defined(USE_TCP_LOOPBACK)
- // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
- // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
- // It would be nice if we could find a solution to this problem
- if (unlink(MDNS_UDS_SERVERPATH))
- debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
+ // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
+ // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
+ // It would be nice if we could find a solution to this problem
+ if (unlink(MDNS_UDS_SERVERPATH))
+ debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
#endif
- }
+ }
- if (PID_FILE[0]) unlink(PID_FILE);
+ if (PID_FILE[0]) unlink(PID_FILE);
- return 0;
- }
+ return 0;
+ }
mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req)
- {
- char prefix[16];
- if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> ");
- else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
-
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
-
- if (!req->terminate)
- LogMsgNoIdent("%s No operation yet on this socket", prefix);
- else if (req->terminate == connection_termination)
- {
- int num_records = 0, num_ops = 0;
- const registered_record_entry *p;
- const request_state *r;
- for (p = req->u.reg_recs; p; p=p->next) num_records++;
- for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
- LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix,
- num_records, num_records != 1 ? "s" : "",
- num_ops, num_ops != 1 ? "s" : "");
- for (p = req->u.reg_recs; p; p=p->next)
- LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr));
- for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
- }
- else if (req->terminate == regservice_termination_callback)
- {
- service_instance *ptr;
- for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
- LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u",
- (ptr == req->u.servicereg.instances) ? prefix : " ",
- ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs));
- }
- else if (req->terminate == browse_termination_callback)
- {
- browser_t *blist;
- for (blist = req->u.browser.browsers; blist; blist = blist->next)
- LogMsgNoIdent("%s DNSServiceBrowse %##s", (blist == req->u.browser.browsers) ? prefix : " ", blist->q.qname.c);
- }
- else if (req->terminate == resolve_termination_callback)
- LogMsgNoIdent("%s DNSServiceResolve %##s", prefix, req->u.resolve.qsrv.qname.c);
- else if (req->terminate == queryrecord_termination_callback)
- LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype));
- else if (req->terminate == enum_termination_callback)
- LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c);
- else if (req->terminate == port_mapping_termination_callback)
- LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d",
- prefix,
- &req->u.pm.NATinfo.ExternalAddress,
- req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ",
- req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ",
- mDNSVal16(req->u.pm.NATinfo.IntPort),
- mDNSVal16(req->u.pm.ReqExt),
- mDNSVal16(req->u.pm.NATinfo.ExternalPort),
- req->u.pm.NATinfo.NATLease,
- req->u.pm.NATinfo.Lifetime);
- else if (req->terminate == addrinfo_termination_callback)
- LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s", prefix,
- req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ",
- req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ",
- req->u.addrinfo.q4.qname.c);
- else
- LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
- }
+ {
+ char prefix[16];
+ if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> ");
+ else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
+
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+
+ if (!req->terminate)
+ LogMsgNoIdent("%s No operation yet on this socket", prefix);
+ else if (req->terminate == connection_termination)
+ {
+ int num_records = 0, num_ops = 0;
+ const registered_record_entry *p;
+ const request_state *r;
+ for (p = req->u.reg_recs; p; p=p->next) num_records++;
+ for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
+ LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix,
+ num_records, num_records != 1 ? "s" : "",
+ num_ops, num_ops != 1 ? "s" : "");
+ for (p = req->u.reg_recs; p; p=p->next)
+ LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr));
+ for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u",
+ (ptr == req->u.servicereg.instances) ? prefix : " ",
+ ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs));
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ for (blist = req->u.browser.browsers; blist; blist = blist->next)
+ LogMsgNoIdent("%s DNSServiceBrowse %##s", (blist == req->u.browser.browsers) ? prefix : " ", blist->q.qname.c);
+ }
+ else if (req->terminate == resolve_termination_callback)
+ LogMsgNoIdent("%s DNSServiceResolve %##s", prefix, req->u.resolve.qsrv.qname.c);
+ else if (req->terminate == queryrecord_termination_callback)
+ LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype));
+ else if (req->terminate == enum_termination_callback)
+ LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c);
+ else if (req->terminate == port_mapping_termination_callback)
+ LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d",
+ prefix,
+ &req->u.pm.NATinfo.ExternalAddress,
+ req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ",
+ req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ",
+ mDNSVal16(req->u.pm.NATinfo.IntPort),
+ mDNSVal16(req->u.pm.ReqExt),
+ mDNSVal16(req->u.pm.NATinfo.ExternalPort),
+ req->u.pm.NATinfo.NATLease,
+ req->u.pm.NATinfo.Lifetime);
+ else if (req->terminate == addrinfo_termination_callback)
+ LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s", prefix,
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ",
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ",
+ req->u.addrinfo.q4.qname.c);
+ else
+ LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
+ }
mDNSlocal char *RecordTypeName(mDNSu8 rtype)
- {
- switch (rtype)
- {
- case kDNSRecordTypeUnregistered: return ("Unregistered ");
- case kDNSRecordTypeDeregistering: return ("Deregistering");
- case kDNSRecordTypeUnique: return ("Unique ");
- case kDNSRecordTypeAdvisory: return ("Advisory ");
- case kDNSRecordTypeShared: return ("Shared ");
- case kDNSRecordTypeVerified: return ("Verified ");
- case kDNSRecordTypeKnownUnique: return ("KnownUnique ");
- default: return("Unknown");
- }
- }
+ {
+ switch (rtype)
+ {
+ case kDNSRecordTypeUnregistered: return ("Unregistered ");
+ case kDNSRecordTypeDeregistering: return ("Deregistering");
+ case kDNSRecordTypeUnique: return ("Unique ");
+ case kDNSRecordTypeAdvisory: return ("Advisory ");
+ case kDNSRecordTypeShared: return ("Shared ");
+ case kDNSRecordTypeVerified: return ("Verified ");
+ case kDNSRecordTypeKnownUnique: return ("KnownUnique ");
+ default: return("Unknown");
+ }
+ }
mDNSlocal void LogEtcHosts(mDNS *const m)
- {
- mDNSBool showheader = mDNStrue;
- const AuthRecord *ar;
- mDNSu32 slot;
- AuthGroup *ag;
- int count = 0;
- int authslot = 0;
- mDNSBool truncated = 0;
-
- for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
- {
- if (m->rrauth.rrauth_hash[slot]) authslot++;
- for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
- for (ar = ag->members; ar; ar = ar->next)
- {
- if (ar->RecordCallback != FreeEtcHosts) continue;
- if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
-
- // Print a maximum of 50 records
- if (count++ >= 50) { truncated = mDNStrue; continue; }
- if (ar->ARType == AuthRecordLocalOnly)
- {
- if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
- LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
- else
- {
- mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
- LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
- }
- }
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- }
-
- if (showheader) LogMsgNoIdent("<None>");
- else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
- }
+ {
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+ int count = 0;
+ int authslot = 0;
+ mDNSBool truncated = 0;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ if (m->rrauth.rrauth_hash[slot]) authslot++;
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback != FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 50 records
+ if (count++ >= 50) { truncated = mDNStrue; continue; }
+ if (ar->ARType == AuthRecordLocalOnly)
+ {
+ if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else
+ {
+ mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
+ LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
+ }
+ }
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+ else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
+ }
mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
- {
- mDNSBool showheader = mDNStrue;
- const AuthRecord *ar;
- mDNSu32 slot;
- AuthGroup *ag;
-
- for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
- {
- for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
- for (ar = ag->members; ar; ar = ar->next)
- {
- if (ar->RecordCallback == FreeEtcHosts) continue;
- if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
-
- // Print a maximum of 400 records
- if (ar->ARType == AuthRecordLocalOnly)
- LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
- else if (ar->ARType == AuthRecordP2P)
- LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- }
-
- if (showheader) LogMsgNoIdent("<None>");
- }
+ {
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback == FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 400 records
+ if (ar->ARType == AuthRecordLocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else if (ar->ARType == AuthRecordP2P)
+ LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+ }
mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
- {
- mDNSBool showheader = mDNStrue;
- const AuthRecord *ar;
- OwnerOptData owner = zeroOwner;
- for (ar = ResourceRecords; ar; ar=ar->next)
- {
- const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID);
- if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL))
- {
- if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); }
- if (proxy) (*proxy)++;
- if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)))
- {
- owner = ar->WakeUp;
- if (owner.password.l[0])
- LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
- else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC))
- LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq);
- else
- LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq);
- }
- if (AuthRecord_uDNS(ar))
- LogMsgNoIdent("%7d %7d %7d %7d %s",
- ar->ThisAPInterval / mDNSPlatformOneSecond,
- (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
- ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
- ar->state, ARDisplayString(m, ar));
- else if (ar->ARType == AuthRecordLocalOnly)
- LogMsgNoIdent(" LO %s", ARDisplayString(m, ar));
- else if (ar->ARType == AuthRecordP2P)
- LogMsgNoIdent(" PP %s", ARDisplayString(m, ar));
- else
- LogMsgNoIdent("%7d %7d %7d %7s %s",
- ar->ThisAPInterval / mDNSPlatformOneSecond,
- ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
- ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0,
- ifname ? ifname : "ALL",
- ARDisplayString(m, ar));
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- }
- if (showheader) LogMsgNoIdent("<None>");
- }
+ {
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ OwnerOptData owner = zeroOwner;
+ for (ar = ResourceRecords; ar; ar=ar->next)
+ {
+ const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID);
+ if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL))
+ {
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); }
+ if (proxy) (*proxy)++;
+ if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)))
+ {
+ owner = ar->WakeUp;
+ if (owner.password.l[0])
+ LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
+ else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC))
+ LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq);
+ else
+ LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq);
+ }
+ if (AuthRecord_uDNS(ar))
+ LogMsgNoIdent("%7d %7d %7d %7d %s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
+ ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+ ar->state, ARDisplayString(m, ar));
+ else if (ar->ARType == AuthRecordLocalOnly)
+ LogMsgNoIdent(" LO %s", ARDisplayString(m, ar));
+ else if (ar->ARType == AuthRecordP2P)
+ LogMsgNoIdent(" PP %s", ARDisplayString(m, ar));
+ else
+ LogMsgNoIdent("%7d %7d %7d %7s %s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+ ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0,
+ ifname ? ifname : "ALL",
+ ARDisplayString(m, ar));
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+ if (showheader) LogMsgNoIdent("<None>");
+ }
mDNSexport void udsserver_info(mDNS *const m)
- {
- const mDNSs32 now = mDNS_TimeNow(m);
- mDNSu32 CacheUsed = 0, CacheActive = 0, slot;
- int ProxyA = 0, ProxyD = 0;
- const CacheGroup *cg;
- const CacheRecord *cr;
- const DNSQuestion *q;
- const DNameListElem *d;
- const SearchListElem *s;
-
- LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
-
- LogMsgNoIdent("------------ Cache -------------");
- LogMsgNoIdent("Slt Q TTL if U Type rdlen");
- for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
- for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
- {
- CacheUsed++; // Count one cache entity for the CacheGroup object
- for (cr = cg->members; cr; cr=cr->next)
- {
- const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
- const char *ifname;
- mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
- if (!InterfaceID && cr->resrec.rDNSServer)
- InterfaceID = cr->resrec.rDNSServer->interface;
- ifname = InterfaceNameForID(m, InterfaceID);
- CacheUsed++;
- if (cr->CRActiveQuestion) CacheActive++;
- LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s",
- slot,
- cr->CRActiveQuestion ? "*" : " ",
- remain,
- ifname ? ifname : "-U-",
- (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" :
- (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
- DNSTypeName(cr->resrec.rrtype),
- CRDisplayString(m, cr));
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- }
-
- if (m->rrcache_totalused != CacheUsed)
- LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
- if (m->rrcache_active != CacheActive)
- LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
- LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive);
-
- LogMsgNoIdent("--------- Auth Records ---------");
- LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
-
- LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
- LogLocalOnlyAuthRecords(m);
-
- LogMsgNoIdent("--------- /etc/hosts ---------");
- LogEtcHosts(m);
-
- LogMsgNoIdent("------ Duplicate Records -------");
- LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
-
- LogMsgNoIdent("----- Auth Records Proxied -----");
- LogAuthRecords(m, now, m->ResourceRecords, &ProxyA);
-
- LogMsgNoIdent("-- Duplicate Records Proxied ---");
- LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD);
-
- LogMsgNoIdent("---------- Questions -----------");
- if (!m->Questions) LogMsgNoIdent("<None>");
- else
- {
- CacheUsed = 0;
- CacheActive = 0;
- LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name");
- for (q = m->Questions; q; q=q->next)
- {
- mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
- mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond;
- char *ifname = InterfaceNameForID(m, q->InterfaceID);
- CacheUsed++;
- if (q->ThisQInterval) CacheActive++;
- LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s",
- i, n,
- ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
- mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
- PrivateQuery(q) ? "P" : " ",
- q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
- q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
- }
-
- LogMsgNoIdent("----- Local-Only Questions -----");
- if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>");
- else for (q = m->LocalOnlyQuestions; q; q=q->next)
- LogMsgNoIdent(" %5d %-6s%##s%s",
- q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
-
- LogMsgNoIdent("---- Active Client Requests ----");
- if (!all_requests) LogMsgNoIdent("<None>");
- else
- {
- const request_state *req, *r;
- for (req = all_requests; req; req=req->next)
- {
- if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
- {
- for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent;
- LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd);
- }
- // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
- LogClientInfo(m, req);
- foundparent:;
- }
- }
-
- LogMsgNoIdent("-------- NAT Traversals --------");
- if (!m->NATTraversals) LogMsgNoIdent("<None>");
- else
- {
- const NATTraversalInfo *nat;
- for (nat = m->NATTraversals; nat; nat=nat->next)
- {
- if (nat->Protocol)
- LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d",
- nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP",
- mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result,
- nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
- nat->retryInterval / mDNSPlatformOneSecond,
- nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0);
- else
- LogMsgNoIdent("%p Address Request Retry %5d Interval %5d", nat,
- (m->retryGetAddr - now) / mDNSPlatformOneSecond,
- m->retryIntervalGetAddr / mDNSPlatformOneSecond);
- usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
- }
- }
-
- LogMsgNoIdent("--------- AuthInfoList ---------");
- if (!m->AuthInfoList) LogMsgNoIdent("<None>");
- else
- {
- const DomainAuthInfo *a;
- for (a = m->AuthInfoList; a; a = a->next)
- LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : "");
- }
-
- #if APPLE_OSX_mDNSResponder
- LogMsgNoIdent("--------- TunnelClients --------");
- if (!m->TunnelClients) LogMsgNoIdent("<None>");
- else
- {
- const ClientTunnel *c;
- for (c = m->TunnelClients; c; c = c->next)
- LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
- c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
- }
- #endif // APPLE_OSX_mDNSResponder
-
- LogMsgNoIdent("---------- Misc State ----------");
-
- LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC);
-
- LogMsgNoIdent("m->SleepState %d (%s) seq %d",
- m->SleepState,
- m->SleepState == SleepState_Awake ? "Awake" :
- m->SleepState == SleepState_Transferring ? "Transferring" :
- m->SleepState == SleepState_Sleeping ? "Sleeping" : "?",
- m->SleepSeqNum);
-
- if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
- else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
-
- if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
+ {
+ const mDNSs32 now = mDNS_TimeNow(m);
+ mDNSu32 CacheUsed = 0, CacheActive = 0, slot;
+ int ProxyA = 0, ProxyD = 0;
+ const CacheGroup *cg;
+ const CacheRecord *cr;
+ const DNSQuestion *q;
+ const DNameListElem *d;
+ const SearchListElem *s;
+
+ LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
+
+ LogMsgNoIdent("------------ Cache -------------");
+ LogMsgNoIdent("Slt Q TTL if U Type rdlen");
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
+ {
+ CacheUsed++; // Count one cache entity for the CacheGroup object
+ for (cr = cg->members; cr; cr=cr->next)
+ {
+ const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
+ const char *ifname;
+ mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
+ if (!InterfaceID && cr->resrec.rDNSServer)
+ InterfaceID = cr->resrec.rDNSServer->interface1;
+ ifname = InterfaceNameForID(m, InterfaceID);
+ CacheUsed++;
+ if (cr->CRActiveQuestion) CacheActive++;
+ LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s",
+ slot,
+ cr->CRActiveQuestion ? "*" : " ",
+ remain,
+ ifname ? ifname : "-U-",
+ (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" :
+ (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+ DNSTypeName(cr->resrec.rrtype),
+ CRDisplayString(m, cr));
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ if (m->rrcache_totalused != CacheUsed)
+ LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
+ if (m->rrcache_active != CacheActive)
+ LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
+ LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive);
+
+ LogMsgNoIdent("--------- Auth Records ---------");
+ LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
+
+ LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
+ LogLocalOnlyAuthRecords(m);
+
+ LogMsgNoIdent("--------- /etc/hosts ---------");
+ LogEtcHosts(m);
+
+ LogMsgNoIdent("------ Duplicate Records -------");
+ LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
+
+ LogMsgNoIdent("----- Auth Records Proxied -----");
+ LogAuthRecords(m, now, m->ResourceRecords, &ProxyA);
+
+ LogMsgNoIdent("-- Duplicate Records Proxied ---");
+ LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD);
+
+ LogMsgNoIdent("---------- Questions -----------");
+ if (!m->Questions) LogMsgNoIdent("<None>");
+ else
+ {
+ CacheUsed = 0;
+ CacheActive = 0;
+ LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name");
+ for (q = m->Questions; q; q=q->next)
+ {
+ mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
+ mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond;
+ char *ifname = InterfaceNameForID(m, q->InterfaceID);
+ CacheUsed++;
+ if (q->ThisQInterval) CacheActive++;
+ LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s",
+ i, n,
+ ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
+ mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
+ PrivateQuery(q) ? "P" : " ",
+ q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
+ q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
+ }
+
+ LogMsgNoIdent("----- Local-Only Questions -----");
+ if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>");
+ else for (q = m->LocalOnlyQuestions; q; q=q->next)
+ LogMsgNoIdent(" %5d %-6s%##s%s",
+ q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
+
+ LogMsgNoIdent("---- Active Client Requests ----");
+ if (!all_requests) LogMsgNoIdent("<None>");
+ else
+ {
+ const request_state *req, *r;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+ {
+ for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent;
+ LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd);
+ }
+ // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+ LogClientInfo(m, req);
+ foundparent:;
+ }
+ }
+
+ LogMsgNoIdent("-------- NAT Traversals --------");
+ if (!m->NATTraversals) LogMsgNoIdent("<None>");
+ else
+ {
+ const NATTraversalInfo *nat;
+ for (nat = m->NATTraversals; nat; nat=nat->next)
+ {
+ if (nat->Protocol)
+ LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d",
+ nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP",
+ mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result,
+ nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
+ nat->retryInterval / mDNSPlatformOneSecond,
+ nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0);
+ else
+ LogMsgNoIdent("%p Address Request Retry %5d Interval %5d", nat,
+ (m->retryGetAddr - now) / mDNSPlatformOneSecond,
+ m->retryIntervalGetAddr / mDNSPlatformOneSecond);
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ LogMsgNoIdent("--------- AuthInfoList ---------");
+ if (!m->AuthInfoList) LogMsgNoIdent("<None>");
+ else
+ {
+ const DomainAuthInfo *a;
+ for (a = m->AuthInfoList; a; a = a->next)
+ LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : "");
+ }
+
+ #if APPLE_OSX_mDNSResponder
+ LogMsgNoIdent("--------- TunnelClients --------");
+ if (!m->TunnelClients) LogMsgNoIdent("<None>");
+ else
+ {
+ const ClientTunnel *c;
+ for (c = m->TunnelClients; c; c = c->next)
+ LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
+ c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
+ }
+ #endif // APPLE_OSX_mDNSResponder
+
+ LogMsgNoIdent("---------- Misc State ----------");
+
+ LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC);
+
+ LogMsgNoIdent("m->SleepState %d (%s) seq %d",
+ m->SleepState,
+ m->SleepState == SleepState_Awake ? "Awake" :
+ m->SleepState == SleepState_Transferring ? "Transferring" :
+ m->SleepState == SleepState_Sleeping ? "Sleeping" : "?",
+ m->SleepSeqNum);
+
+ if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
+ else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
+
+ if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d != %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords);
- LogMsgNoIdent("------ Auto Browse Domains -----");
- if (!AutoBrowseDomains) LogMsgNoIdent("<None>");
- else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
-
- LogMsgNoIdent("--- Auto Registration Domains --");
- if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
- else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
-
- LogMsgNoIdent("--- Search Domains --");
- if (!SearchList) LogMsgNoIdent("<None>");
- else
- {
- for (s=SearchList; s; s=s->next)
- {
- char *ifname = InterfaceNameForID(m, s->InterfaceID);
- LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
- }
- }
-
- LogMsgNoIdent("---- Task Scheduling Timers ----");
-
- if (!m->NewQuestions)
- LogMsgNoIdent("NewQuestion <NONE>");
- else
- LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)",
- m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now,
- m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
-
- if (!m->NewLocalOnlyQuestions)
- LogMsgNoIdent("NewLocalOnlyQuestions <NONE>");
- else
- LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)",
- m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
-
- if (!m->NewLocalRecords)
- LogMsgNoIdent("NewLocalRecords <NONE>");
- else
- LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords));
-
- LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
- LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>");
- LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6);
- LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn);
- LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut);
+ LogMsgNoIdent("------ Auto Browse Domains -----");
+ if (!AutoBrowseDomains) LogMsgNoIdent("<None>");
+ else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+ LogMsgNoIdent("--- Auto Registration Domains --");
+ if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
+ else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+ LogMsgNoIdent("--- Search Domains --");
+ if (!SearchList) LogMsgNoIdent("<None>");
+ else
+ {
+ for (s=SearchList; s; s=s->next)
+ {
+ char *ifname = InterfaceNameForID(m, s->InterfaceID);
+ LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
+ }
+ }
+
+ LogMsgNoIdent("---- Task Scheduling Timers ----");
+
+ if (!m->NewQuestions)
+ LogMsgNoIdent("NewQuestion <NONE>");
+ else
+ LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)",
+ m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now,
+ m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
+
+ if (!m->NewLocalOnlyQuestions)
+ LogMsgNoIdent("NewLocalOnlyQuestions <NONE>");
+ else
+ LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)",
+ m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
+
+ if (!m->NewLocalRecords)
+ LogMsgNoIdent("NewLocalRecords <NONE>");
+ else
+ LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords));
+
+ LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
+ LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>");
+ LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6);
+ LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn);
+ LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut);
#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now)
- LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)");
- LogMsgNoIdent("m->timenow %08X %11d", now, now);
- LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust);
- LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent);
+ LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)");
+ LogMsgNoIdent("m->timenow %08X %11d", now, now);
+ LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust);
+ LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent);
#ifndef UNICAST_DISABLED
- LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent);
- LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate);
- LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp);
- LogTimer("m->retryGetAddr ", m->retryGetAddr);
+ LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent);
+ LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate);
+ LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp);
+ LogTimer("m->retryGetAddr ", m->retryGetAddr);
#endif
- LogTimer("m->NextCacheCheck ", m->NextCacheCheck);
- LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS);
- LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry);
- LogTimer("m->DelaySleep ", m->DelaySleep);
+ LogTimer("m->NextCacheCheck ", m->NextCacheCheck);
+ LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS);
+ LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry);
+ LogTimer("m->DelaySleep ", m->DelaySleep);
- LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery);
- LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe);
- LogTimer("m->NextScheduledResponse", m->NextScheduledResponse);
+ LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery);
+ LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe);
+ LogTimer("m->NextScheduledResponse", m->NextScheduledResponse);
- LogTimer("m->SuppressSending ", m->SuppressSending);
- LogTimer("m->SuppressProbes ", m->SuppressProbes);
- LogTimer("m->ProbeFailTime ", m->ProbeFailTime);
- LogTimer("m->DelaySleep ", m->DelaySleep);
- LogTimer("m->SleepLimit ", m->SleepLimit);
- LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
- }
+ LogTimer("m->SuppressSending ", m->SuppressSending);
+ LogTimer("m->SuppressProbes ", m->SuppressProbes);
+ LogTimer("m->ProbeFailTime ", m->ProbeFailTime);
+ LogTimer("m->DelaySleep ", m->DelaySleep);
+ LogTimer("m->SleepLimit ", m->SleepLimit);
+ LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
+ }
#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
mDNSexport void uds_validatelists(void)
- {
- const request_state *req, *p;
- for (req = all_requests; req; req=req->next)
- {
- if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2))
- LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd);
-
- if (req->primary == req)
- LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd);
-
- if (req->primary && req->replies)
- LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)",
- req, req->sd, req->primary && req->replies);
-
- p = req->primary;
- if ((long)p & 3)
- LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd);
- else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2)))
- LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd);
-
- reply_state *rep;
- for (rep = req->replies; rep; rep=rep->next)
- if (rep->next == (reply_state *)~0)
- LogMemCorruption("UDS req->replies: %p is garbage", rep);
-
- if (req->terminate == connection_termination)
- {
- registered_record_entry *r;
- for (r = req->u.reg_recs; r; r=r->next)
- if (r->next == (registered_record_entry *)~0)
- LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r);
- }
- else if (req->terminate == regservice_termination_callback)
- {
- service_instance *s;
- for (s = req->u.servicereg.instances; s; s=s->next)
- if (s->next == (service_instance *)~0)
- LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s);
- }
- else if (req->terminate == browse_termination_callback)
- {
- browser_t *b;
- for (b = req->u.browser.browsers; b; b=b->next)
- if (b->next == (browser_t *)~0)
- LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b);
- }
- }
-
- DNameListElem *d;
- for (d = SCPrefBrowseDomains; d; d=d->next)
- if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
- LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
-
- ARListElem *b;
- for (b = LocalDomainEnumRecords; b; b=b->next)
- if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63)
- LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]);
-
- for (d = AutoBrowseDomains; d; d=d->next)
- if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
- LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
-
- for (d = AutoRegistrationDomains; d; d=d->next)
- if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
- LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]);
- }
+ {
+ const request_state *req, *p;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2))
+ LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd);
+
+ if (req->primary == req)
+ LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd);
+
+ if (req->primary && req->replies)
+ LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)",
+ req, req->sd, req->primary && req->replies);
+
+ p = req->primary;
+ if ((long)p & 3)
+ LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd);
+ else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2)))
+ LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd);
+
+ reply_state *rep;
+ for (rep = req->replies; rep; rep=rep->next)
+ if (rep->next == (reply_state *)~0)
+ LogMemCorruption("UDS req->replies: %p is garbage", rep);
+
+ if (req->terminate == connection_termination)
+ {
+ registered_record_entry *r;
+ for (r = req->u.reg_recs; r; r=r->next)
+ if (r->next == (registered_record_entry *)~0)
+ LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *s;
+ for (s = req->u.servicereg.instances; s; s=s->next)
+ if (s->next == (service_instance *)~0)
+ LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s);
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *b;
+ for (b = req->u.browser.browsers; b; b=b->next)
+ if (b->next == (browser_t *)~0)
+ LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b);
+ }
+ }
+
+ DNameListElem *d;
+ for (d = SCPrefBrowseDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+ ARListElem *b;
+ for (b = LocalDomainEnumRecords; b; b=b->next)
+ if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63)
+ LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]);
+
+ for (d = AutoBrowseDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+ for (d = AutoRegistrationDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]);
+ }
#endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
mDNSlocal int send_msg(request_state *const req)
- {
- reply_state *const rep = req->replies; // Send the first waiting reply
- ssize_t nwriten;
- if (req->no_reply) return(t_complete);
-
- ConvertHeaderBytes(rep->mhdr);
- nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0);
- ConvertHeaderBytes(rep->mhdr);
-
- if (nwriten < 0)
- {
- if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0;
- else
- {
+ {
+ reply_state *const rep = req->replies; // Send the first waiting reply
+ ssize_t nwriten;
+ if (req->no_reply) return(t_complete);
+
+ ConvertHeaderBytes(rep->mhdr);
+ nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0);
+ ConvertHeaderBytes(rep->mhdr);
+
+ if (nwriten < 0)
+ {
+ if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0;
+ else
+ {
#if !defined(PLATFORM_NO_EPIPE)
- if (dnssd_errno == EPIPE)
- return(req->ts = t_terminated);
- else
+ if (dnssd_errno == EPIPE)
+ return(req->ts = t_terminated);
+ else
#endif
- {
- LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)",
- rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
- return(t_error);
- }
- }
- }
- rep->nwriten += nwriten;
- return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming;
- }
+ {
+ LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)",
+ rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ return(t_error);
+ }
+ }
+ }
+ rep->nwriten += nwriten;
+ return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming;
+ }
mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
- {
- mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
- request_state **req = &all_requests;
-
- while (*req)
- {
- request_state *const r = *req;
-
- if (r->terminate == resolve_termination_callback)
- if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0)
- {
- r->u.resolve.ReportTime = 0;
- LogMsgNoIdent("Client application bug: DNSServiceResolve(%##s) active for over two minutes. "
- "This places considerable burden on the network.", r->u.resolve.qsrv.qname.c);
- }
-
- // Note: Only primary req's have reply lists, not subordinate req's.
- while (r->replies) // Send queued replies
- {
- transfer_state result;
- if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
- result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading
- if (result == t_complete)
- {
- reply_state *fptr = r->replies;
- r->replies = r->replies->next;
- freeL("reply_state/udsserver_idle", fptr);
- r->time_blocked = 0; // reset failure counter after successful send
- r->unresponsiveness_reports = 0;
- continue;
- }
- else if (result == t_terminated || result == t_error)
- {
- LogMsg("%3d: Could not write data to client because of error - aborting connection", r->sd);
- LogClientInfo(&mDNSStorage, r);
- abort_request(r);
- }
- break;
- }
-
- if (r->replies) // If we failed to send everything, check our time_blocked timer
- {
- if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond;
-
- if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0;
- else if (!r->time_blocked) r->time_blocked = NonZeroTime(now);
- else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1))
- {
- int num = 0;
- struct reply_state *x = r->replies;
- while (x) { num++; x=x->next; }
- LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting",
- r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
- if (++r->unresponsiveness_reports >= 60)
- {
- LogMsg("%3d: Client unresponsive; aborting connection", r->sd);
- LogClientInfo(&mDNSStorage, r);
- abort_request(r);
- }
- }
- }
-
- if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory
- {
- // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
- *req = r->next;
- freeL("request_state/udsserver_idle", r);
- }
- else
- req = &r->next;
- }
- return nextevent;
- }
+ {
+ mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
+ request_state **req = &all_requests;
+
+ while (*req)
+ {
+ request_state *const r = *req;
+
+ if (r->terminate == resolve_termination_callback)
+ if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0)
+ {
+ r->u.resolve.ReportTime = 0;
+ LogMsgNoIdent("Client application bug: DNSServiceResolve(%##s) active for over two minutes. "
+ "This places considerable burden on the network.", r->u.resolve.qsrv.qname.c);
+ }
+
+ // Note: Only primary req's have reply lists, not subordinate req's.
+ while (r->replies) // Send queued replies
+ {
+ transfer_state result;
+ if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
+ result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading
+ if (result == t_complete)
+ {
+ reply_state *fptr = r->replies;
+ r->replies = r->replies->next;
+ freeL("reply_state/udsserver_idle", fptr);
+ r->time_blocked = 0; // reset failure counter after successful send
+ r->unresponsiveness_reports = 0;
+ continue;
+ }
+ else if (result == t_terminated || result == t_error)
+ {
+ LogMsg("%3d: Could not write data to client because of error - aborting connection", r->sd);
+ LogClientInfo(&mDNSStorage, r);
+ abort_request(r);
+ }
+ break;
+ }
+
+ if (r->replies) // If we failed to send everything, check our time_blocked timer
+ {
+ if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond;
+
+ if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0;
+ else if (!r->time_blocked) r->time_blocked = NonZeroTime(now);
+ else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1))
+ {
+ int num = 0;
+ struct reply_state *x = r->replies;
+ while (x) { num++; x=x->next; }
+ LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting",
+ r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
+ if (++r->unresponsiveness_reports >= 60)
+ {
+ LogMsg("%3d: Client unresponsive; aborting connection", r->sd);
+ LogClientInfo(&mDNSStorage, r);
+ abort_request(r);
+ }
+ }
+ }
+
+ if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ *req = r->next;
+ freeL("request_state/udsserver_idle", r);
+ }
+ else
+ req = &r->next;
+ }
+ return nextevent;
+ }
struct CompileTimeAssertionChecks_uds_daemon
- {
- // Check our structures are reasonable sizes. Including overly-large buffers, or embedding
- // other overly-large structures instead of having a pointer to them, can inadvertently
- // cause structure sizes (and therefore memory usage) to balloon unreasonably.
- char sizecheck_request_state [(sizeof(request_state) <= 1784) ? 1 : -1];
- char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1];
- char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1];
- char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1];
- char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1];
- char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1];
- };
+ {
+ // Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+ // other overly-large structures instead of having a pointer to them, can inadvertently
+ // cause structure sizes (and therefore memory usage) to balloon unreasonably.
+ char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1];
+ char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1];
+ char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1];
+ char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1];
+ char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1];
+ char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1];
+ };
diff --git a/src/tools/qmlprofilertool/commandlistener.cpp b/src/tools/qmlprofilertool/commandlistener.cpp
deleted file mode 100644
index 57754a6dd0..0000000000
--- a/src/tools/qmlprofilertool/commandlistener.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "commandlistener.h"
-#include "constants.h"
-#include <QTextStream>
-
-CommandListener::CommandListener(QObject *parent)
- : QThread(parent)
- , m_stopRequested(false)
-{
-}
-
-void CommandListener::run()
-{
- QString line;
- QTextStream in(stdin, QIODevice::ReadOnly);
- do {
- line = in.readLine();
- line = line.trimmed();
- if (!line.isEmpty()) {
- emit command(line);
- if (line == QLatin1String(Constants::CMD_QUIT)
- || line == QLatin1String(Constants::CMD_QUIT2))
- return;
- }
- } while (!m_stopRequested && !line.isNull());
-}
diff --git a/src/tools/qmlprofilertool/qmlprofilerapplication.h b/src/tools/qmlprofilertool/qmlprofilerapplication.h
deleted file mode 100644
index 9414895631..0000000000
--- a/src/tools/qmlprofilertool/qmlprofilerapplication.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** GNU Lesser General Public License Usage
-**
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this file.
-** Please review the following information to ensure the GNU Lesser General
-** Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** Other Usage
-**
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**************************************************************************/
-
-#ifndef QMLPROFILERAPPLICATION_H
-#define QMLPROFILERAPPLICATION_H
-
-#include <QCoreApplication>
-#include <QStringList>
-#include <QTimer>
-
-#include <qdeclarativedebugclient.h>
-#include <qmlprofilertraceclient.h>
-#include <qmlprofilereventlist.h>
-#include <qv8profilerclient.h>
-
-QT_FORWARD_DECLARE_CLASS(QProcess)
-
-class QmlProfilerApplication : public QCoreApplication
-{
- Q_OBJECT
-public:
- QmlProfilerApplication(int &argc, char **argv);
- ~QmlProfilerApplication();
-
- bool parseArguments();
- void printUsage();
- int exec();
-
-public slots:
- void userCommand(const QString &command);
-
-private slots:
- void run();
- void tryToConnect();
- void connected();
- void connectionStateChanged(QAbstractSocket::SocketState state);
- void connectionError(QAbstractSocket::SocketError error);
- void processHasOutput();
- void processFinished();
-
- void traceClientEnabled();
- void profilerClientEnabled();
- void traceFinished();
- void recordingChanged();
-
- void print(const QString &line);
- void logError(const QString &error);
- void logStatus(const QString &status);
-
- void qmlComplete();
- void v8Complete();
-
-private:
- void printCommands();
- QString traceFileName() const;
-
- enum ApplicationMode {
- LaunchMode,
- AttachMode
- } m_runMode;
-
- // LaunchMode
- QString m_programPath;
- QStringList m_programArguments;
- QProcess *m_process;
- QString m_tracePrefix;
-
- QString m_hostName;
- quint16 m_port;
- bool m_verbose;
- bool m_quitAfterSave;
-
- QmlJsDebugClient::QDeclarativeDebugConnection m_connection;
- QmlJsDebugClient::QmlProfilerTraceClient m_qmlProfilerClient;
- QmlJsDebugClient::QV8ProfilerClient m_v8profilerClient;
- QmlJsDebugClient::QmlProfilerEventList m_eventList;
- QTimer m_connectTimer;
- uint m_connectionAttempts;
-
- bool m_qmlDataReady;
- bool m_v8DataReady;
-};
-
-#endif // QMLPROFILERAPPLICATION_H
diff --git a/src/tools/qmlprofilertool/qmlprofilertool.pro b/src/tools/qmlprofilertool/qmlprofilertool.pro
deleted file mode 100644
index ca582eeb76..0000000000
--- a/src/tools/qmlprofilertool/qmlprofilertool.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-QT = core
-include(../../../qtcreator.pri)
-include(../../rpath.pri)
-
-TEMPLATE = app
-TARGET = qmlprofiler
-DESTDIR = $$IDE_BIN_PATH
-
-CONFIG += console
-CONFIG -= app_bundle
-
-include(../../shared/symbianutils/symbianutils.pri)
-include(../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri)
-
-INCLUDEPATH += ../../libs/qmljsdebugclient
-
-SOURCES += main.cpp \
- qmlprofilerapplication.cpp \
- commandlistener.cpp
-
-HEADERS += \
- qmlprofilerapplication.h \
- commandlistener.h \
- constants.h
-
-target.path=/bin
-INSTALLS+=target
-
diff --git a/src/tools/screenshotcropper/screenshotcropperwindow.h b/src/tools/screenshotcropper/screenshotcropperwindow.h
index ea1f8c8912..5a6746aa80 100644
--- a/src/tools/screenshotcropper/screenshotcropperwindow.h
+++ b/src/tools/screenshotcropper/screenshotcropperwindow.h
@@ -58,7 +58,7 @@ public slots:
void saveData();
private:
- AreasOfInterest m_areasOfInterest;
+ QMap<QString, QRect> m_areasOfInterest;
QString m_areasOfInterestFile;
QString m_imagesFolder;
Ui::ScreenShotCropperWindow *ui;
diff --git a/src/tools/tools.pro b/src/tools/tools.pro
index bd5bac6c8f..648638f706 100644
--- a/src/tools/tools.pro
+++ b/src/tools/tools.pro
@@ -1,11 +1,11 @@
TEMPLATE = subdirs
SUBDIRS = qtpromaker \
- qmlprofilertool \
qmlpuppet
win32 {
SUBDIRS += qtcdebugger
+ !*g++*:SUBDIRS += mdnssd
# win64interrupt only make sense for 64bit builds
ENV_CPU=$$(CPU)
ENV_LIBPATH=$$(LIBPATH)