diff options
author | samelian <samelian@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-05-22 20:12:04 +0000 |
---|---|---|
committer | samelian <samelian@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-05-22 20:12:04 +0000 |
commit | ac87680632b4fb6582d1391b042eff7f0305c0a2 (patch) | |
tree | bfeee57d104a1bbc7c387d35190fa55d692115b7 /kopete | |
parent | aca844682f86c04f6b67b23de2a820fb0c63a32e (diff) | |
download | tdenetwork-ac87680632b4fb6582d1391b042eff7f0305c0a2.tar.gz tdenetwork-ac87680632b4fb6582d1391b042eff7f0305c0a2.zip |
[kdenetwork/kopete] added cmake support
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1233119 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete')
306 files changed, 11986 insertions, 57066 deletions
diff --git a/kopete/CMakeLists.txt b/kopete/CMakeLists.txt new file mode 100644 index 00000000..f04465a7 --- /dev/null +++ b/kopete/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# NOTE some files was renamed: +# kopetemessagemanager.h -> kopetechatsession.h +# kopetemessagemanagerfactory.h -> kopetechatsessionmanager.h + +include( ConfigureChecks.cmake ) + +add_subdirectory( libkopete ) +add_subdirectory( kopete ) +add_subdirectory( protocols ) +add_subdirectory( plugins ) +add_subdirectory( icons ) +add_subdirectory( sounds ) +add_subdirectory( styles ) diff --git a/kopete/ConfigureChecks.cmake b/kopete/ConfigureChecks.cmake new file mode 100644 index 00000000..6a78d91f --- /dev/null +++ b/kopete/ConfigureChecks.cmake @@ -0,0 +1,20 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# glib-2.0 (jabber, msn) +if( (BUILD_KOPETE_PROTOCOL_JABBER AND WITH_JINGLE) OR (BUILD_KOPETE_PROTOCOL_MSN AND WITH_WEBCAM) ) + pkg_search_module( GLIB2 glib-2.0 ) + if( GLIB2_FOUND ) + set( HAVE_GLIB 1 CACHE INTERNAL "" FORCE ) + else( ) + tde_message_fatal( "glib-2.0 is required, but was not found on your system" ) + endif( ) +endif( ) diff --git a/kopete/icons/CMakeLists.txt b/kopete/icons/CMakeLists.txt new file mode 100644 index 00000000..189a3a20 --- /dev/null +++ b/kopete/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons() diff --git a/kopete/kopete/CMakeLists.txt b/kopete/kopete/CMakeLists.txt new file mode 100644 index 00000000..b88714c2 --- /dev/null +++ b/kopete/kopete/CMakeLists.txt @@ -0,0 +1,65 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( addaccountwizard ) +add_subdirectory( addcontactwizard ) +add_subdirectory( contactlist ) +add_subdirectory( chatwindow ) +add_subdirectory( config ) +add_subdirectory( kconf_update ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_BINARY_DIR}/kopete/libkopete + ${CMAKE_BINARY_DIR}/kopete/kopete/addcontactwizard + ${CMAKE_BINARY_DIR}/kopete/kopete/contactlist + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete/avdevice + ${CMAKE_SOURCE_DIR}/kopete/kopete/addaccountwizard + ${CMAKE_SOURCE_DIR}/kopete/kopete/addcontactwizard + ${CMAKE_SOURCE_DIR}/kopete/kopete/contactlist + ${CMAKE_SOURCE_DIR}/kopete/kopete/config/plugins + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +tde_install_icons( ) +install( FILES kopete.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install( FILES kopeteui.rc eventsrc DESTINATION ${DATA_INSTALL_DIR}/kopete ) +install( FILES x-kopete-emoticons.desktop DESTINATION ${MIME_INSTALL_DIR}/application ) + + +##### kopete (executable) ####################### + +tde_add_executable( kopete AUTOMOC + SOURCES + main.cpp kopeteapplication.cpp kopeteiface.cpp + kopeteiface.skel systemtray.cpp kopeteballoon.cpp + kopetewindow.cpp kopeteaccountstatusbaricon.cpp + kimifaceimpl.cpp kimiface.skel groupkabcselectorwidget.ui + kopeteeditglobalidentitywidget.cpp + LINK + kopetecontactlist-static kopeteaddaccountwizard-static + kopeteaddcontactwizard-static kopeteui-static + kopetepluginconfig-static kopete-shared + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/kopete/kopete/addaccountwizard/CMakeLists.txt b/kopete/kopete/addaccountwizard/CMakeLists.txt new file mode 100644 index 00000000..4c25f42a --- /dev/null +++ b/kopete/kopete/addaccountwizard/CMakeLists.txt @@ -0,0 +1,27 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteaddaccountwizard (static) ########### + +tde_add_library( kopeteaddaccountwizard STATIC_PIC AUTOMOC + SOURCES + addaccountwizardpage1.ui addaccountwizardpage2.ui + addaccountwizardpage3.ui addaccountwizard.cpp +) diff --git a/kopete/kopete/addcontactwizard/CMakeLists.txt b/kopete/kopete/addcontactwizard/CMakeLists.txt new file mode 100644 index 00000000..ce2c317c --- /dev/null +++ b/kopete/kopete/addcontactwizard/CMakeLists.txt @@ -0,0 +1,27 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteaddcontactwizard (static) ########### + +tde_add_library( kopeteaddcontactwizard STATIC_PIC AUTOMOC + SOURCES + addcontactwizard_base.ui addcontactwizard.cpp +) diff --git a/kopete/kopete/chatwindow/CMakeLists.txt b/kopete/kopete/chatwindow/CMakeLists.txt new file mode 100644 index 00000000..c88678d6 --- /dev/null +++ b/kopete/kopete/chatwindow/CMakeLists.txt @@ -0,0 +1,71 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/kopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopeterichtexteditpartfull.rc DESTINATION ${DATA_INSTALL_DIR}/kopeterichtexteditpart ) +install( FILES kopetechatwindow.rc kopeteemailwindow.rc DESTINATION ${DATA_INSTALL_DIR}/kopete ) +install( FILES chatwindow.desktop emailwindow.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### libkrichtexteditpart (module) ############# + +tde_add_kpart( libkrichtexteditpart AUTOMOC + SOURCES krichtexteditpart.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kopete_chatwindow (module) ################ + +tde_add_kpart( kopete_chatwindow AUTOMOC + SOURCES + chatview.cpp kopetechatwindow.cpp chatmemberslistwidget.cpp + LINK kopeteui-static kopetechatwindow-static kopete-shared khtml-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kopete_emailwindow (module) ############### + +tde_add_kpart( kopete_emailwindow AUTOMOC + SOURCES + kopeteemailwindow.cpp + LINK kopeteui-static kopetechatwindow-static kopete-shared khtml-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kopetechatwindow (static) ################# + +tde_add_library( kopetechatwindow STATIC_PIC AUTOMOC + SOURCES + chatmessagepart.cpp emoticonselector.cpp kopeteemoticonaction.cpp + chattexteditpart.cpp krichtexteditpart.cpp kopetechatwindowstyle.cpp + kopetechatwindowstylemanager.cpp +) diff --git a/kopete/kopete/chatwindow/krichtexteditpart.cpp b/kopete/kopete/chatwindow/krichtexteditpart.cpp index a08fa7e3..392e187a 100644 --- a/kopete/kopete/chatwindow/krichtexteditpart.cpp +++ b/kopete/kopete/chatwindow/krichtexteditpart.cpp @@ -38,7 +38,7 @@ public: KopeteRichTextEditPart::KopeteRichTextEditPart( TQWidget *wparent, const char *wname, TQObject*, const char*, const TQStringList& ) : KParts::ReadOnlyPart( wparent, wname ? wname : "rich_text_part" ) { - KopeteRichTextEditPart::KopeteRichTextEditPart( wparent, wname, false ); + KopeteRichTextEditPart( wparent, wname, false ); } KopeteRichTextEditPart::KopeteRichTextEditPart( TQWidget *parent, const char *name, int capabilities ) diff --git a/kopete/kopete/config/CMakeLists.txt b/kopete/kopete/config/CMakeLists.txt new file mode 100644 index 00000000..88e5efd9 --- /dev/null +++ b/kopete/kopete/config/CMakeLists.txt @@ -0,0 +1,17 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( plugins ) +add_subdirectory( accounts ) +add_subdirectory( behavior ) +add_subdirectory( appearance ) +add_subdirectory( identity ) +add_subdirectory( avdevice ) diff --git a/kopete/kopete/config/accounts/CMakeLists.txt b/kopete/kopete/config/accounts/CMakeLists.txt new file mode 100644 index 00000000..53741c13 --- /dev/null +++ b/kopete/kopete/config/accounts/CMakeLists.txt @@ -0,0 +1,37 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/kopete/addaccountwizard + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_accountconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kcm_kopete_accountconfig (module) ######### + +tde_add_kpart( kcm_kopete_accountconfig AUTOMOC + SOURCES kopeteaccountconfigbase.ui kopeteaccountconfig.cpp + LINK kopeteaddaccountwizard-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/kopete/config/appearance/CMakeLists.txt b/kopete/kopete/config/appearance/CMakeLists.txt new file mode 100644 index 00000000..f24d022b --- /dev/null +++ b/kopete/kopete/config/appearance/CMakeLists.txt @@ -0,0 +1,43 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/kopete/chatwindow + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_appearanceconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kcm_kopete_appearanceconfig (module) ###### + +tde_add_kpart( kcm_kopete_appearanceconfig AUTOMOC + SOURCES + appearanceconfig.cpp appearanceconfig_chatwindow.ui + appearanceconfig_colors.ui appearanceconfig_contactlist.ui + appearanceconfig_emoticons.ui emoticonseditdialog.cpp + emoticonseditwidget.ui tooltipeditdialog.cpp + tooltipeditwidget.ui + LINK kopetechatwindow-static kopete-shared khtml-shared knewstuff-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/kopete/config/avdevice/CMakeLists.txt b/kopete/kopete/config/avdevice/CMakeLists.txt new file mode 100644 index 00000000..7fe58f0e --- /dev/null +++ b/kopete/kopete/config/avdevice/CMakeLists.txt @@ -0,0 +1,39 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete/avdevice + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) +install( FILES kopete_avdeviceconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kcm_kopete_avdeviceconfig (module) ######## + +tde_add_kpart( kcm_kopete_avdeviceconfig AUTOMOC + SOURCES + avdeviceconfig.cpp avdeviceconfig_videoconfig.ui + LINK kopete_videodevice-shared kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/kopete/config/behavior/CMakeLists.txt b/kopete/kopete/config/behavior/CMakeLists.txt new file mode 100644 index 00000000..669878c0 --- /dev/null +++ b/kopete/kopete/config/behavior/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_behaviorconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kcm_kopete_behaviorconfig (module) ######## + +tde_add_kpart( kcm_kopete_behaviorconfig AUTOMOC + SOURCES + kopeteawayconfigbase.ui behaviorconfig_chat.ui + behaviorconfig_general.ui behaviorconfig_events.ui + behaviorconfig.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/kopete/config/identity/CMakeLists.txt b/kopete/kopete/config/identity/CMakeLists.txt new file mode 100644 index 00000000..2495b388 --- /dev/null +++ b/kopete/kopete/config/identity/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_identityconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopeteidentityconfigpreferences.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + + +##### kcm_kopete_identityconfig (module) ######## + +tde_add_kpart( kcm_kopete_identityconfig AUTOMOC + SOURCES + kopeteidentityconfigbase.ui kopeteidentityconfig.cpp + globalidentitiesmanager.cpp kopeteidentityconfigpreferences.kcfgc + LINK kopeteui-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/kopete/config/plugins/CMakeLists.txt b/kopete/kopete/config/plugins/CMakeLists.txt new file mode 100644 index 00000000..58fbefc3 --- /dev/null +++ b/kopete/kopete/config/plugins/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetepluginconfig (static) ############### + +tde_add_library( kopetepluginconfig STATIC_PIC AUTOMOC + SOURCES kopetepluginconfig.cpp +) diff --git a/kopete/kopete/contactlist/CMakeLists.txt b/kopete/kopete/contactlist/CMakeLists.txt new file mode 100644 index 00000000..741d3e0f --- /dev/null +++ b/kopete/kopete/contactlist/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME no XRENDER support yet + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_BINARY_DIR}/kopete/kopete/addcontactwizard + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/kopete + ${CMAKE_SOURCE_DIR}/kopete/kopete/addcontactwizard + ${CMAKE_SOURCE_DIR}/kopete/kopete/chatwindow + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetecontactlist (static) ################ + +tde_add_library( kopetecontactlist STATIC_PIC AUTOMOC + SOURCES + kopetemetacontactlvi.cpp kopetestatusgroupviewitem.cpp + kopetegroupviewitem.cpp kopetecontactlistview.cpp + kopetegvipropswidget.ui kopetemetalvipropswidget.ui + kopetelviprops.cpp kopeteaddrbookexport.cpp + kopeteaddrbookexportui.ui customnotifications.ui + customnotificationprops.cpp kopetegrouplistaction.cpp + kabcexport.cpp kabcexport_base.ui +) diff --git a/kopete/kopete/contactlist/kopetegroupviewitem.cpp b/kopete/kopete/contactlist/kopetegroupviewitem.cpp index ab375540..b28b7cb3 100644 --- a/kopete/kopete/contactlist/kopetegroupviewitem.cpp +++ b/kopete/kopete/contactlist/kopetegroupviewitem.cpp @@ -174,8 +174,7 @@ void KopeteGroupViewItem::refreshDisplayName() // Sorting in this slot is extremely expensive as it's called dozens of times and // the sorting itself is rather slow. Therefore we call delayedSort, which tries // to group multiple sort requests into one. - using namespace Kopete::UI::ListView; - if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) ) + if ( Kopete::UI::ListView::ListView *lv = dynamic_cast<Kopete::UI::ListView::ListView *>( listView() ) ) lv->delayedSort(); else listView()->sort(); diff --git a/kopete/kopete/kconf_update/CMakeLists.txt b/kopete/kopete/kconf_update/CMakeLists.txt new file mode 100644 index 00000000..1317d824 --- /dev/null +++ b/kopete/kopete/kconf_update/CMakeLists.txt @@ -0,0 +1,64 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( -DKDE_NO_COMPAT ) + +include_directories( + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete-pluginloader.upd kopete-account-kconf_update.upd + kopete-pluginloader2.upd kopete-jabberproxytype-kconf_update.upd + kopete-jabberpriorityaddition-kconf_update.upd + kopete-nameTracking.upd + DESTINATION ${KCONF_UPDATE_INSTALL_DIR} ) + +install( PROGRAMS + kopete-pluginloader.pl kopete-jabberpriorityaddition-kconf_update.sh + kopete-pluginloader2.sh kopete-jabberproxytype-kconf_update.sh + kopete-account-0.10.pl kopete-account-kconf_update.sh + DESTINATION ${KCONF_UPDATE_INSTALL_DIR} ) + + +##### kopete_account_kconf_update (executable) ## + +tde_add_executable( kopete_account_kconf_update + SOURCES kopete-account-kconf_update.cpp + LINK ${TQT_LIBRARIES} + DESTINATION ${LIB_INSTALL_DIR}/kconf_update_bin +) + + +##### kopete_pluginloader2_kconf_update (executable) + +tde_add_executable( kopete_pluginloader2_kconf_update + SOURCES kopete-pluginloader2.cpp + LINK ${TQT_LIBRARIES} + DESTINATION ${LIB_INSTALL_DIR}/kconf_update_bin +) + + +##### kopete_nameTracking_kconf_update (executable) + +tde_add_executable( kopete_nameTracking_kconf_update + SOURCES kopete-nameTracking.cpp + LINK kdecore-shared + DESTINATION ${LIB_INSTALL_DIR}/kconf_update_bin +) diff --git a/kopete/libkopete/CMakeLists.txt b/kopete/libkopete/CMakeLists.txt new file mode 100644 index 00000000..bbed2548 --- /dev/null +++ b/kopete/libkopete/CMakeLists.txt @@ -0,0 +1,91 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME no support for XSS and XRENDER yet + +add_subdirectory( private ) +add_subdirectory( ui ) +add_subdirectory( avdevice ) + +add_definitions( -DKDE_NO_COMPAT ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/private + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### headers ################################### + +install( FILES + kopeteaccount.h kopeteaccountmanager.h kopeteawayaction.h + kopeteawaydialog.h kopeteaway.h kopetecommandhandler.h + kopetecontact.h kopetecontactlistelement.h kopetecontactlist.h + kopetecontactproperty.h kopeteeventpresentation.h + kopete_export.h kopeteglobal.h kopetegroup.h kopetemessageevent.h + kopetemessage.h kopetemessagehandlerchain.h kopetemessagehandler.h + kopetechatsession.h kopetechatsessionmanager.h kopetemetacontact.h + kopetemimetypehandler.h kopeteonlinestatus.h kopeteonlinestatusmanager.h + kopetepasswordedaccount.h kopetepassword.h kopeteplugin.h + kopeteprotocol.h kopetesimplemessagehandler.h kopetetask.h + kopetetransfermanager.h kopeteuiglobal.h kabcpersistence.h + managedconnectionaccount.h kopetenotifydataobject.h + kopeteversion.h kopeteprefs.h kopetepicture.h webcamwidget.h + kopetepluginmanager.h + DESTINATION ${INCLUDE_INSTALL_DIR}/kopete ) + + +##### other data ################################ + +install( FILES kopete.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) +install( FILES kopetecommandui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete ) +install( FILES kopeteplugin.desktop kopeteprotocol.desktop kopeteui.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) + + +##### kopete (shared) ########################### + +tde_include_tqt( kopetemimetypehandler.cpp ) + +tde_add_library( kopete SHARED AUTOMOC + SOURCES + knotification.cpp connectionmanager.cpp kopeteonlinestatus.cpp + kopeteonlinestatusmanager.cpp kopeteprotocol.cpp + kopetecontact.cpp kopetepluginmanager.cpp kopeteplugin.cpp + kopetemessage.cpp kopetechatsession.cpp kopetechatsessionmanager.cpp + kopetecontactlist.cpp kopetemetacontact.cpp kopeteawaydialog.cpp + kopetetransfermanager.cpp kopetegroup.cpp kcautoconfigmodule.cpp + kopeteaccountmanager.cpp kopeteaccount.cpp kopetecontactlistelement.cpp + kopetecommandhandler.cpp kopeteaway.cpp kopeteawayaction.cpp + kautoconfig.cpp kopetewalletmanager.cpp kopetecontactproperty.cpp + kopetepassword.cpp kopeteglobal.cpp kopeteuiglobal.cpp + kopetepasswordedaccount.cpp kopetemimetypehandler.cpp + kopetetask.cpp kopetemimesourcefactory.cpp kopeteeventpresentation.cpp + kopetenotifyevent.cpp kopetenotifydataobject.cpp kopeteblacklister.cpp + kopetemessageevent.cpp kopetemessagehandler.cpp kopetemessagehandlerchain.cpp + kopetesimplemessagehandler.cpp kopeteproperties.cpp kabcpersistence.cpp + connectionmanager.skel clientiface.stub managedconnectionaccount.cpp + networkstatuscommon.h kopeteconfig.kcfgc kopeteutils.cpp + kopeteprefs.cpp kopetepicture.cpp webcamwidget.cpp + VERSION 1.0.0 + EMBED kopeteui-static + LINK kopeteprivate-static kabc-shared kutils-shared khtml-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kopete/libkopete/avdevice/CMakeLists.txt b/kopete/libkopete/avdevice/CMakeLists.txt new file mode 100644 index 00000000..8e37773f --- /dev/null +++ b/kopete/libkopete/avdevice/CMakeLists.txt @@ -0,0 +1,46 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME no support for GLLIB yet + +add_definitions( -DKDE_NO_COMPAT ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### kvideoio (static) ######################### + +tde_add_library( kvideoio STATIC_PIC AUTOMOC + SOURCES kxv.cpp qvideo.cpp qvideostream.cpp +) + + +##### kopete_videodevice (shared) ############### + +tde_add_library( kopete_videodevice SHARED AUTOMOC + SOURCES + videocontrol.cpp videodevice.cpp videodevicemodelpool.cpp + videodevicepool.cpp videoinput.cpp bayer.cpp + sonix_compress.cpp + VERSION 1.0.0 + LINK kdecore-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kopete/libkopete/kopetemessagemanager.h b/kopete/libkopete/kopetemessagemanager.h deleted file mode 100644 index a07fb6f9..00000000 --- a/kopete/libkopete/kopetemessagemanager.h +++ /dev/null @@ -1,3 +0,0 @@ -#warning kopetemessagemanager.h has been renamed to kopetechatsession.h -#include "kopetechatsession.h" - diff --git a/kopete/libkopete/kopetemessagemanagerfactory.h b/kopete/libkopete/kopetemessagemanagerfactory.h deleted file mode 100644 index 9ebdaa98..00000000 --- a/kopete/libkopete/kopetemessagemanagerfactory.h +++ /dev/null @@ -1,3 +0,0 @@ -#warning kopetemessagemanagerfactory.h has been renamed to kopetechatsessionmanager.h -#include "kopetechatsessionmanager.h" - diff --git a/kopete/libkopete/kopetepasswordedaccount.h b/kopete/libkopete/kopetepasswordedaccount.h index d008bae4..dbec92db 100644 --- a/kopete/libkopete/kopetepasswordedaccount.h +++ b/kopete/libkopete/kopetepasswordedaccount.h @@ -21,8 +21,6 @@ #include "kopete_export.h" -class Kopete::OnlineStatus; - namespace Kopete { diff --git a/kopete/libkopete/private/CMakeLists.txt b/kopete/libkopete/private/CMakeLists.txt new file mode 100644 index 00000000..9ed6d643 --- /dev/null +++ b/kopete/libkopete/private/CMakeLists.txt @@ -0,0 +1,29 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( -DKDE_NO_COMPAT ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteprivate (static) #################### + +tde_add_library( kopeteprivate STATIC_PIC AUTOMOC + SOURCES + kopeteemoticons.cpp kopetecommand.cpp + kopeteviewmanager.cpp kopeteutils_private.cpp +) diff --git a/kopete/libkopete/ui/CMakeLists.txt b/kopete/libkopete/ui/CMakeLists.txt new file mode 100644 index 00000000..cb224847 --- /dev/null +++ b/kopete/libkopete/ui/CMakeLists.txt @@ -0,0 +1,56 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( -DKDE_NO_COMPAT ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### other data ################################ + +install( FILES + addressbooklinkwidget.h kopetefileconfirmdialog.h + kopetepasswordwidget.h kopeteview.h addcontactpage.h + kopeteviewplugin.h editaccountwidget.h kopetecontactaction.h + kopetestdaction.h userinfodialog.h addressbookselectordialog.h + addressbookselectorwidget.h kopetelistview.h accountselector.h + kopetelistviewitem.h kopetelistviewsearchline.h + ${CMAKE_CURRENT_BINARY_DIR}/fileconfirmbase.h + ${CMAKE_CURRENT_BINARY_DIR}/kopeteawaydialogbase.h + ${CMAKE_CURRENT_BINARY_DIR}/kopetepasswordwidgetbase.h + ${CMAKE_CURRENT_BINARY_DIR}/kopetepassworddialog.h + DESTINATION ${INCLUDE_INSTALL_DIR}/kopete/ui ) + + +##### kopeteui (static) ######################### + +tde_add_library( kopeteui STATIC_PIC AUTOMOC + SOURCES + kopetecontactaction.cpp addcontactpage.cpp editaccountwidget.cpp + kopetepassworddialog.ui kopetestdaction.cpp kopeteawaydialogbase.ui + kopetefileconfirmdialog.cpp fileconfirmbase.ui + userinfodialog.cpp kopeteview.cpp kopetepasswordwidgetbase.ui + kopetepasswordwidget.cpp accountselector.cpp kopeteviewplugin.cpp + addresseeitem.cpp addressbookselectorwidget_base.ui + addressbookselectordialog.cpp addressbookselectorwidget.cpp + metacontactselectorwidget_base.ui metacontactselectorwidget.cpp + kopetelistview.cpp kopetelistviewitem.cpp kopetelistviewsearchline.cpp + contactaddednotifywidget.ui contactaddednotifydialog.cpp + addressbooklinkwidget_base.ui addressbooklinkwidget.cpp +) diff --git a/kopete/plugins/CMakeLists.txt b/kopete/plugins/CMakeLists.txt new file mode 100644 index 00000000..a5c6b08c --- /dev/null +++ b/kopete/plugins/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_LATEX latex ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_AUTOREPLACE autoreplace ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_HISTORY history ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_CONTACTNOTES contactnotes ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_CRYPTOGRAPHY cryptography ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_CONNECTIONSTATUS connectionstatus ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_TRANSLATOR translator ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_NOWLISTENING nowlistening ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_WEBPRESENCE webpresence ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_TEXTEFFECT texteffect ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_HIGHLIGHT highlight ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_ALIAS alias ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_MOTIONAUTOAWAY motionautoaway ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_NETMEETING netmeeting ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_ADDBOOKMARKS addbookmarks ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_STATISTICS statistics ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_SMPPPDCS smpppdcs ) diff --git a/kopete/plugins/addbookmarks/CMakeLists.txt b/kopete/plugins/addbookmarks/CMakeLists.txt new file mode 100644 index 00000000..4d1df111 --- /dev/null +++ b/kopete/plugins/addbookmarks/CMakeLists.txt @@ -0,0 +1,49 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_addbookmarks.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_addbookmarks_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kopete_addbookmarks (module) ############## + +tde_add_kpart( kopete_addbookmarks AUTOMOC + SOURCES + addbookmarksplugin.cpp addbookmarksprefssettings.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_addbookmarks (module) ########## + +tde_add_kpart( kcm_kopete_addbookmarks AUTOMOC + SOURCES + addbookmarkspreferences.cpp addbookmarksprefsui.ui + addbookmarksprefssettings.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/alias/CMakeLists.txt b/kopete/plugins/alias/CMakeLists.txt new file mode 100644 index 00000000..d8ca2088 --- /dev/null +++ b/kopete/plugins/alias/CMakeLists.txt @@ -0,0 +1,50 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_alias.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_alias_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kopete_alias (module) ##################### + +tde_add_kpart( kopete_alias AUTOMOC + SOURCES + aliasplugin.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_alias (module) ################# + +tde_add_kpart( kcm_kopete_alias AUTOMOC + SOURCES + aliaspreferences.cpp aliasdialogbase.ui + aliasdialog.ui editaliasdialog.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/alias/aliasplugin.cpp b/kopete/plugins/alias/aliasplugin.cpp index 52a8511b..4824e2fb 100644 --- a/kopete/plugins/alias/aliasplugin.cpp +++ b/kopete/plugins/alias/aliasplugin.cpp @@ -9,7 +9,7 @@ #include <kgenericfactory.h> -#include "kopetemessagemanagerfactory.h" +#include "kopetechatsessionmanager.h" #include "aliasplugin.h" diff --git a/kopete/plugins/autoreplace/CMakeLists.txt b/kopete/plugins/autoreplace/CMakeLists.txt new file mode 100644 index 00000000..9a39da64 --- /dev/null +++ b/kopete/plugins/autoreplace/CMakeLists.txt @@ -0,0 +1,49 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_autoreplace.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_autoreplace_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kopete_autoreplace (module) ############### + +tde_add_kpart( kopete_autoreplace AUTOMOC + SOURCES + autoreplaceplugin.cpp autoreplaceconfig.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_autoreplace (module) ########### + +tde_add_kpart( kcm_kopete_autoreplace AUTOMOC + SOURCES + autoreplacepreferences.cpp autoreplaceconfig.cpp autoreplaceprefs.ui + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/autoreplace/icons/CMakeLists.txt b/kopete/plugins/autoreplace/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/plugins/autoreplace/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/connectionstatus/CMakeLists.txt b/kopete/plugins/connectionstatus/CMakeLists.txt new file mode 100644 index 00000000..207349c5 --- /dev/null +++ b/kopete/plugins/connectionstatus/CMakeLists.txt @@ -0,0 +1,35 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_connectionstatus.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_connectionstatus (module) ########## + +tde_add_kpart( kopete_connectionstatus AUTOMOC + SOURCES connectionstatusplugin.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/contactnotes/CMakeLists.txt b/kopete/plugins/contactnotes/CMakeLists.txt new file mode 100644 index 00000000..284e59bd --- /dev/null +++ b/kopete/plugins/contactnotes/CMakeLists.txt @@ -0,0 +1,36 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_contactnotes.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES contactnotesui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_contactnotes ) + + +##### kopete_contactnotes (module) ############## + +tde_add_kpart( kopete_contactnotes AUTOMOC + SOURCES contactnotesplugin.cpp contactnotesedit.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/cryptography/CMakeLists.txt b/kopete/plugins/cryptography/CMakeLists.txt new file mode 100644 index 00000000..4952d8e2 --- /dev/null +++ b/kopete/plugins/cryptography/CMakeLists.txt @@ -0,0 +1,53 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_cryptography.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_cryptography_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES cryptographyui.rc cryptographychatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_cryptography ) + + +##### kopete_cryptography (module) ############## + +tde_add_kpart( kopete_cryptography AUTOMOC + SOURCES + cryptographyplugin.cpp kgpginterface.cpp + cryptographyguiclient.cpp cryptographyselectuserkey.cpp + cryptographyuserkey_ui.ui popuppublic.cpp kgpgselkey.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_cryptography (module) ########## + +tde_add_kpart( kcm_kopete_cryptography AUTOMOC + SOURCES + cryptographypreferences.cpp cryptographyprefsbase.ui + kgpgselkey.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/cryptography/icons/CMakeLists.txt b/kopete/plugins/cryptography/icons/CMakeLists.txt new file mode 100644 index 00000000..a3136b6f --- /dev/null +++ b/kopete/plugins/cryptography/icons/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/cryptography/kgpgselkey.h b/kopete/plugins/cryptography/kgpgselkey.h index 20b3dfd2..1e357482 100644 --- a/kopete/plugins/cryptography/kgpgselkey.h +++ b/kopete/plugins/cryptography/kgpgselkey.h @@ -26,7 +26,7 @@ class KListView; class TQCheckBox; -typedef struct gpgKey{ +struct gpgKey { TQString gpgkeymail; TQString gpgkeyname; TQString gpgkeyid; diff --git a/kopete/plugins/highlight/CMakeLists.txt b/kopete/plugins/highlight/CMakeLists.txt new file mode 100644 index 00000000..312252bf --- /dev/null +++ b/kopete/plugins/highlight/CMakeLists.txt @@ -0,0 +1,52 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_highlight.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_highlight_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kopete_highlight (module) ################# + +tde_add_kpart( kopete_highlight AUTOMOC + SOURCES + highlightplugin.cpp highlightconfig.cpp filter.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_highlight (module) ############# + +tde_add_kpart( kcm_kopete_highlight AUTOMOC + SOURCES + highlightprefsbase.ui highlightpreferences.cpp + filter.cpp highlightconfig.cpp + LINK kutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/highlight/icons/CMakeLists.txt b/kopete/plugins/highlight/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/plugins/highlight/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/history/CMakeLists.txt b/kopete/plugins/history/CMakeLists.txt new file mode 100644 index 00000000..4d118d91 --- /dev/null +++ b/kopete/plugins/history/CMakeLists.txt @@ -0,0 +1,55 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( ${KDE_PLUGIN} ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_history.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES historyui.rc historychatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_history ) +install( FILES kopete_history_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES historyconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + + +##### kopete_history (module) ################### + +tde_add_kpart( kopete_history AUTOMOC + SOURCES + historyplugin.cpp historydialog.cpp historyviewer.ui + historylogger.cpp converter.cpp historyguiclient.cpp + historyconfig.kcfgc + LINK kopete-shared khtml-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_history (module) ############### + +tde_add_kpart( kcm_kopete_history AUTOMOC + SOURCES + historyprefsui.ui historypreferences.cpp + historyconfig.kcfgc + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/latex/CMakeLists.txt b/kopete/plugins/latex/CMakeLists.txt new file mode 100644 index 00000000..18433acd --- /dev/null +++ b/kopete/plugins/latex/CMakeLists.txt @@ -0,0 +1,53 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_latex.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_latex_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES kopete_latexconvert.sh DESTINATION ${BIN_INSTALL_DIR} ) +install( FILES latexconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) +install( FILES latexchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_latex ) + + +##### kopete_latex (module) ##################### + +tde_add_kpart( kopete_latex AUTOMOC + SOURCES + latexplugin.cpp latexconfig.kcfgc latexguiclient.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_latex (module) ####################### + +tde_add_kpart( kcm_kopete_latex AUTOMOC + SOURCES + latexprefsbase.ui latexpreferences.cpp latexconfig.kcfgc + LINK kutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/latex/icons/CMakeLists.txt b/kopete/plugins/latex/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/plugins/latex/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/motionautoaway/CMakeLists.txt b/kopete/plugins/motionautoaway/CMakeLists.txt new file mode 100644 index 00000000..bc750e89 --- /dev/null +++ b/kopete/plugins/motionautoaway/CMakeLists.txt @@ -0,0 +1,51 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_motionaway.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_motionaway_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES motionawayconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + + +##### kopete_motionaway (module) ################ + +tde_add_kpart( kopete_motionaway AUTOMOC + SOURCES + motionawayplugin.cpp motionawayconfig.kcfgc + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_motionaway (module) ############ + +tde_add_kpart( kcm_kopete_motionaway AUTOMOC + SOURCES + motionawayprefs.ui motionawaypreferences.cpp + motionawayconfig.kcfgc + LINK kutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/netmeeting/CMakeLists.txt b/kopete/plugins/netmeeting/CMakeLists.txt new file mode 100644 index 00000000..b6cc0313 --- /dev/null +++ b/kopete/plugins/netmeeting/CMakeLists.txt @@ -0,0 +1,56 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +if( NOT BUILD_KOPETE_PROTOCOL_MSN ) + tde_message_error( "netmeeting plugin needs msn protocol.\n Add -DBUILD_KOPETE_PROTOCOL_MSN=ON to cmake flags." ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/protocols/msn + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_netmeeting.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_netmeeting_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES netmeetingchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_netmeeting ) + + +##### kopete_netmeeting (module) ################ + +tde_add_kpart( kopete_netmeeting AUTOMOC + SOURCES + netmeetingplugin.cpp netmeetinginvitation.cpp + netmeetingguiclient.cpp + LINK kopete_msn_shared-shared kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_netmeeting (module) ############ + +tde_add_kpart( kcm_kopete_netmeeting AUTOMOC + SOURCES + netmeetingprefs_ui.ui netmeetingpreferences.cpp + LINK kutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/nowlistening/CMakeLists.txt b/kopete/plugins/nowlistening/CMakeLists.txt new file mode 100644 index 00000000..b1b6b198 --- /dev/null +++ b/kopete/plugins/nowlistening/CMakeLists.txt @@ -0,0 +1,54 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_nowlistening.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_nowlistening_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES nowlisteningconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) +install( FILES nowlisteningui.rc nowlisteningchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete ) + + +##### kopete_nowlistening (module) ############## + +tde_add_kpart( kopete_nowlistening AUTOMOC + SOURCES + nowlisteningconfig.kcfgc nowlisteningplugin.cpp nlkscd.cpp + nlnoatun.cpp nlxmms.cpp nowlisteningguiclient.cpp nljuk.cpp + nlamarok.cpp nlkaffeine.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_nowlistening (module) ########## + +tde_add_kpart( kcm_kopete_nowlistening AUTOMOC + SOURCES + nowlisteningprefs.ui nowlisteningpreferences.cpp + nowlisteningconfig.kcfgc + LINK kutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/smpppdcs/CMakeLists.txt b/kopete/plugins/smpppdcs/CMakeLists.txt new file mode 100644 index 00000000..cac131f9 --- /dev/null +++ b/kopete/plugins/smpppdcs/CMakeLists.txt @@ -0,0 +1,59 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) +add_subdirectory( libsmpppdclient ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/libsmpppdclient + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_smpppdcs.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_smpppdcs_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES smpppdcs.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + + +##### kopete_smpppdcs (module) ################## + +tde_add_kpart( kopete_smpppdcs AUTOMOC + SOURCES + kinternetiface.stub smpppdcsplugin.cpp onlineinquiry.cpp + smpppdcsiface.skel detectordcop.cpp detectorsmpppd.cpp + detectornetstat.cpp detectornetworkstatus.cpp smpppdcsconfig.kcfgc + LINK smpppdclient-static kopete-shared crypto + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_smpppdcs (module) ############## + +tde_add_kpart( kcm_kopete_smpppdcs AUTOMOC + SOURCES + smpppdcsprefs.ui smpppdcspreferences.cpp smpppdsearcher.cpp + smpppdcsprefsimpl.cpp smpppdlocationui.ui smpppdlocationwidget.cpp + smpppdcsconfig.kcfgc + LINK smpppdclient-static kopete-shared crypto + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/smpppdcs/icons/CMakeLists.txt b/kopete/plugins/smpppdcs/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/plugins/smpppdcs/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/CMakeLists.txt b/kopete/plugins/smpppdcs/libsmpppdclient/CMakeLists.txt new file mode 100644 index 00000000..e56ea8d4 --- /dev/null +++ b/kopete/plugins/smpppdcs/libsmpppdclient/CMakeLists.txt @@ -0,0 +1,23 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### smpppdclient (static) ##################### + +tde_add_library( smpppdclient STATIC_PIC + SOURCES + smpppdclient.cpp smpppdstate.cpp smpppdready.cpp smpppdunsettled.cpp +) diff --git a/kopete/plugins/statistics/CMakeLists.txt b/kopete/plugins/statistics/CMakeLists.txt new file mode 100644 index 00000000..629aa9c7 --- /dev/null +++ b/kopete/plugins/statistics/CMakeLists.txt @@ -0,0 +1,45 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +add_subdirectory( images ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${SQLITE_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_statistics.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES statisticsui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_statistics ) + + +##### kopete_statistics (module) ################ + +tde_add_kpart( kopete_statistics AUTOMOC + SOURCES + statisticsplugin.cpp statisticsdb.cpp statisticsdialog.cpp + statisticswidget.ui statisticscontact.cpp statisticsdcopiface.skel + LINK kopete-shared khtml-shared ${SQLITE_LIBRARIES} + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/statistics/ConfigureChecks.cmake b/kopete/plugins/statistics/ConfigureChecks.cmake new file mode 100644 index 00000000..429214ee --- /dev/null +++ b/kopete/plugins/statistics/ConfigureChecks.cmake @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +pkg_search_module( SQLITE sqlite3 ) +if( NOT SQLITE_FOUND ) + tde_message_fatal( "sqlite3 is required, but was not found on your system" ) +endif( ) diff --git a/kopete/plugins/statistics/images/CMakeLists.txt b/kopete/plugins/statistics/images/CMakeLists.txt new file mode 100644 index 00000000..e411a9af --- /dev/null +++ b/kopete/plugins/statistics/images/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + blue.png navy.png black.png gray.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/pics/statistics ) diff --git a/kopete/plugins/statistics/sqlite/Makefile.am b/kopete/plugins/statistics/sqlite/Makefile.am deleted file mode 100644 index f647c6d5..00000000 --- a/kopete/plugins/statistics/sqlite/Makefile.am +++ /dev/null @@ -1,51 +0,0 @@ -noinst_LTLIBRARIES = \ - libsqlite.la - -KDE_CFLAGS = \ - -w - -libsqlite_la_CFLAGS = \ - $(all_includes) \ - -DTHREADSAFE=1 - -libsqlite_la_LDFLAGS = \ - $(LIBPTHREAD) - -libsqlite_la_SOURCES = \ - attach.c \ - auth.c \ - btree.c \ - build.c \ - date.c \ - delete.c \ - encode.c \ - expr.c \ - func.c \ - hash.c \ - insert.c \ - legacy.c \ - main.c \ - opcodes.c \ - os_mac.c \ - os_unix.c \ - os_win.c \ - pager.c \ - parse.c \ - pragma.c \ - printf.c \ - random.c \ - select.c \ - shell.c \ - table.c \ - tokenize.c \ - trigger.c \ - update.c \ - utf.c \ - util.c \ - vacuum.c \ - vdbe.c \ - vdbeapi.c \ - vdbeaux.c \ - vdbemem.c \ - where.c - diff --git a/kopete/plugins/statistics/sqlite/attach.c b/kopete/plugins/statistics/sqlite/attach.c deleted file mode 100644 index 2f089986..00000000 --- a/kopete/plugins/statistics/sqlite/attach.c +++ /dev/null @@ -1,329 +0,0 @@ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the ATTACH and DETACH commands. -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** This routine is called by the parser to process an ATTACH statement: -** -** ATTACH DATABASE filename AS dbname -** -** The pFilename and pDbname arguments are the tokens that define the -** filename and dbname in the ATTACH statement. -*/ -void sqlite3Attach( - Parse *pParse, /* The parser context */ - Token *pFilename, /* Name of database file */ - Token *pDbname, /* Name of the database to use internally */ - int keyType, /* 0: no key. 1: TEXT, 2: BLOB */ - Token *pKey /* Text of the key for keytype 1 and 2 */ -){ - Db *aNew; - int rc, i; - char *zFile, *zName; - sqlite3 *db; - Vdbe *v; - - v = sqlite3GetVdbe(pParse); - if( !v ) return; - sqlite3VdbeAddOp(v, OP_Halt, 0, 0); - if( pParse->explain ) return; - db = pParse->db; - if( db->nDb>=MAX_ATTACHED+2 ){ - sqlite3ErrorMsg(pParse, "too many attached databases - max %d", - MAX_ATTACHED); - pParse->rc = SQLITE_ERROR; - return; - } - - if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, "cannot ATTACH database within transaction"); - pParse->rc = SQLITE_ERROR; - return; - } - - zFile = sqlite3NameFromToken(pFilename);; - if( zFile==0 ) return; -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){ - sqliteFree(zFile); - return; - } -#endif /* SQLITE_OMIT_AUTHORIZATION */ - - zName = sqlite3NameFromToken(pDbname); - if( zName==0 ) return; - for(i=0; i<db->nDb; i++){ - char *z = db->aDb[i].zName; - if( z && sqlite3StrICmp(z, zName)==0 ){ - sqlite3ErrorMsg(pParse, "database %z is already in use", zName); - pParse->rc = SQLITE_ERROR; - sqliteFree(zFile); - return; - } - } - - if( db->aDb==db->aDbStatic ){ - aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ - aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); - if( aNew==0 ) return; - } - db->aDb = aNew; - aNew = &db->aDb[db->nDb++]; - memset(aNew, 0, sizeof(*aNew)); - sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); - aNew->zName = zName; - aNew->safety_level = 3; - rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); - if( rc ){ - sqlite3ErrorMsg(pParse, "unable to open database: %s", zFile); - } -#if SQLITE_HAS_CODEC - { - extern int sqlite3CodecAttach(sqlite3*, int, void*, int); - char *zKey; - int nKey; - if( keyType==0 ){ - /* No key specified. Use the key from the main database */ - extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - }else if( keyType==1 ){ - /* Key specified as text */ - zKey = sqlite3NameFromToken(pKey); - nKey = strlen(zKey); - }else{ - /* Key specified as a BLOB */ - char *zTemp; - assert( keyType==2 ); - pKey->z++; - pKey->n--; - zTemp = sqlite3NameFromToken(pKey); - zKey = sqlite3HexToBlob(zTemp); - sqliteFree(zTemp); - } - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); - if( keyType ){ - sqliteFree(zKey); - } - } -#endif - sqliteFree(zFile); - db->flags &= ~SQLITE_Initialized; - if( pParse->nErr==0 && rc==SQLITE_OK ){ - rc = sqlite3ReadSchema(pParse); - } - if( rc ){ - int i = db->nDb - 1; - assert( i>=2 ); - if( db->aDb[i].pBt ){ - sqlite3BtreeClose(db->aDb[i].pBt); - db->aDb[i].pBt = 0; - } - sqlite3ResetInternalSchema(db, 0); - if( 0==pParse->nErr ){ - pParse->nErr++; - pParse->rc = SQLITE_ERROR; - } - } -} - -/* -** This routine is called by the parser to process a DETACH statement: -** -** DETACH DATABASE dbname -** -** The pDbname argument is the name of the database in the DETACH statement. -*/ -void sqlite3Detach(Parse *pParse, Token *pDbname){ - int i; - sqlite3 *db; - Vdbe *v; - Db *pDb = 0; - - v = sqlite3GetVdbe(pParse); - if( !v ) return; - sqlite3VdbeAddOp(v, OP_Halt, 0, 0); - if( pParse->explain ) return; - db = pParse->db; - for(i=0; i<db->nDb; i++){ - pDb = &db->aDb[i]; - if( pDb->pBt==0 || pDb->zName==0 ) continue; - if( strlen(pDb->zName)!=pDbname->n ) continue; - if( sqlite3StrNICmp(pDb->zName, pDbname->z, pDbname->n)==0 ) break; - } - if( i>=db->nDb ){ - sqlite3ErrorMsg(pParse, "no such database: %T", pDbname); - return; - } - if( i<2 ){ - sqlite3ErrorMsg(pParse, "cannot detach database %T", pDbname); - return; - } - if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, "cannot DETACH database within transaction"); - pParse->rc = SQLITE_ERROR; - return; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){ - return; - } -#endif /* SQLITE_OMIT_AUTHORIZATION */ - sqlite3BtreeClose(pDb->pBt); - pDb->pBt = 0; - sqlite3ResetInternalSchema(db, 0); -} - -/* -** Initialize a DbFixer structure. This routine must be called prior -** to passing the structure to one of the sqliteFixAAAA() routines below. -** -** The return value indicates whether or not fixation is required. TRUE -** means we do need to fix the database references, FALSE means we do not. -*/ -int sqlite3FixInit( - DbFixer *pFix, /* The fixer to be initialized */ - Parse *pParse, /* Error messages will be written here */ - int iDb, /* This is the database that must be used */ - const char *zType, /* "view", "trigger", or "index" */ - const Token *pName /* Name of the view, trigger, or index */ -){ - sqlite3 *db; - - if( iDb<0 || iDb==1 ) return 0; - db = pParse->db; - assert( db->nDb>iDb ); - pFix->pParse = pParse; - pFix->zDb = db->aDb[iDb].zName; - pFix->zType = zType; - pFix->pName = pName; - return 1; -} - -/* -** The following set of routines walk through the parse tree and assign -** a specific database to all table references where the database name -** was left unspecified in the original SQL statement. The pFix structure -** must have been initialized by a prior call to sqlite3FixInit(). -** -** These routines are used to make sure that an index, trigger, or -** view in one database does not refer to objects in a different database. -** (Exception: indices, triggers, and views in the TEMP database are -** allowed to refer to anything.) If a reference is explicitly made -** to an object in a different database, an error message is added to -** pParse->zErrMsg and these routines return non-zero. If everything -** checks out, these routines return 0. -*/ -int sqlite3FixSrcList( - DbFixer *pFix, /* Context of the fixation */ - SrcList *pList /* The Source list to check and modify */ -){ - int i; - const char *zDb; - struct SrcList_item *pItem; - - if( pList==0 ) return 0; - zDb = pFix->zDb; - for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){ - if( pItem->zDatabase==0 ){ - pItem->zDatabase = sqliteStrDup(zDb); - }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){ - sqlite3ErrorMsg(pFix->pParse, - "%s %T cannot reference objects in database %s", - pFix->zType, pFix->pName, pItem->zDatabase); - return 1; - } - if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; - if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; - } - return 0; -} -int sqlite3FixSelect( - DbFixer *pFix, /* Context of the fixation */ - Select *pSelect /* The SELECT statement to be fixed to one database */ -){ - while( pSelect ){ - if( sqlite3FixExprList(pFix, pSelect->pEList) ){ - return 1; - } - if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pSelect->pWhere) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pSelect->pHaving) ){ - return 1; - } - pSelect = pSelect->pPrior; - } - return 0; -} -int sqlite3FixExpr( - DbFixer *pFix, /* Context of the fixation */ - Expr *pExpr /* The expression to be fixed to one database */ -){ - while( pExpr ){ - if( sqlite3FixSelect(pFix, pExpr->pSelect) ){ - return 1; - } - if( sqlite3FixExprList(pFix, pExpr->pList) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pExpr->pRight) ){ - return 1; - } - pExpr = pExpr->pLeft; - } - return 0; -} -int sqlite3FixExprList( - DbFixer *pFix, /* Context of the fixation */ - ExprList *pList /* The expression to be fixed to one database */ -){ - int i; - struct ExprList_item *pItem; - if( pList==0 ) return 0; - for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){ - if( sqlite3FixExpr(pFix, pItem->pExpr) ){ - return 1; - } - } - return 0; -} -int sqlite3FixTriggerStep( - DbFixer *pFix, /* Context of the fixation */ - TriggerStep *pStep /* The trigger step be fixed to one database */ -){ - while( pStep ){ - if( sqlite3FixSelect(pFix, pStep->pSelect) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pStep->pWhere) ){ - return 1; - } - if( sqlite3FixExprList(pFix, pStep->pExprList) ){ - return 1; - } - pStep = pStep->pNext; - } - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/auth.c b/kopete/plugins/statistics/sqlite/auth.c deleted file mode 100644 index b251eacf..00000000 --- a/kopete/plugins/statistics/sqlite/auth.c +++ /dev/null @@ -1,223 +0,0 @@ -/* -** 2003 January 11 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the sqlite3_set_authorizer() -** API. This facility is an optional feature of the library. Embedded -** systems that do not need this facility may omit it by recompiling -** the library with -DSQLITE_OMIT_AUTHORIZATION=1 -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** All of the code in this file may be omitted by defining a single -** macro. -*/ -#ifndef SQLITE_OMIT_AUTHORIZATION - -/* -** Set or clear the access authorization function. -** -** The access authorization function is be called during the compilation -** phase to verify that the user has read and/or write access permission on -** various fields of the database. The first argument to the auth function -** is a copy of the 3rd argument to this routine. The second argument -** to the auth function is one of these constants: -** -** SQLITE_CREATE_INDEX -** SQLITE_CREATE_TABLE -** SQLITE_CREATE_TEMP_INDEX -** SQLITE_CREATE_TEMP_TABLE -** SQLITE_CREATE_TEMP_TRIGGER -** SQLITE_CREATE_TEMP_VIEW -** SQLITE_CREATE_TRIGGER -** SQLITE_CREATE_VIEW -** SQLITE_DELETE -** SQLITE_DROP_INDEX -** SQLITE_DROP_TABLE -** SQLITE_DROP_TEMP_INDEX -** SQLITE_DROP_TEMP_TABLE -** SQLITE_DROP_TEMP_TRIGGER -** SQLITE_DROP_TEMP_VIEW -** SQLITE_DROP_TRIGGER -** SQLITE_DROP_VIEW -** SQLITE_INSERT -** SQLITE_PRAGMA -** SQLITE_READ -** SQLITE_SELECT -** SQLITE_TRANSACTION -** SQLITE_UPDATE -** -** The third and fourth arguments to the auth function are the name of -** the table and the column that are being accessed. The auth function -** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If -** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY -** means that the SQL statement will never-run - the sqlite3_exec() call -** will return with an error. SQLITE_IGNORE means that the SQL statement -** should run but attempts to read the specified column will return NULL -** and attempts to write the column will be ignored. -** -** Setting the auth function to NULL disables this hook. The default -** setting of the auth function is NULL. -*/ -int sqlite3_set_authorizer( - sqlite3 *db, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pArg -){ - db->xAuth = xAuth; - db->pAuthArg = pArg; - return SQLITE_OK; -} - -/* -** Write an error message into pParse->zErrMsg that explains that the -** user-supplied authorization function returned an illegal value. -*/ -static void sqliteAuthBadReturnCode(Parse *pParse, int rc){ - sqlite3ErrorMsg(pParse, "illegal return value (%d) from the " - "authorization function - should be SQLITE_OK, SQLITE_IGNORE, " - "or SQLITE_DENY", rc); - pParse->rc = SQLITE_ERROR; -} - -/* -** The pExpr should be a TK_COLUMN expression. The table referred to -** is in pTabList or else it is the NEW or OLD table of a trigger. -** Check to see if it is OK to read this particular column. -** -** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN -** instruction into a TK_NULL. If the auth function returns SQLITE_DENY, -** then generate an error. -*/ -void sqlite3AuthRead( - Parse *pParse, /* The parser context */ - Expr *pExpr, /* The expression to check authorization on */ - SrcList *pTabList /* All table that pExpr might refer to */ -){ - sqlite3 *db = pParse->db; - int rc; - Table *pTab; /* The table being read */ - const char *zCol; /* Name of the column of the table */ - int iSrc; /* Index in pTabList->a[] of table being read */ - const char *zDBase; /* Name of database being accessed */ - TriggerStack *pStack; /* The stack of current triggers */ - - if( db->xAuth==0 ) return; - assert( pExpr->op==TK_COLUMN ); - for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; - } - if( iSrc>=0 && iSrc<pTabList->nSrc ){ - pTab = pTabList->a[iSrc].pTab; - }else if( (pStack = pParse->trigStack)!=0 ){ - /* This must be an attempt to read the NEW or OLD pseudo-tables - ** of a trigger. - */ - assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); - pTab = pStack->pTab; - }else{ - return; - } - if( pTab==0 ) return; - if( pExpr->iColumn>=0 ){ - assert( pExpr->iColumn<pTab->nCol ); - zCol = pTab->aCol[pExpr->iColumn].zName; - }else if( pTab->iPKey>=0 ){ - assert( pTab->iPKey<pTab->nCol ); - zCol = pTab->aCol[pTab->iPKey].zName; - }else{ - zCol = "ROWID"; - } - assert( pExpr->iDb<db->nDb ); - zDBase = db->aDb[pExpr->iDb].zName; - rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, - pParse->zAuthContext); - if( rc==SQLITE_IGNORE ){ - pExpr->op = TK_NULL; - }else if( rc==SQLITE_DENY ){ - if( db->nDb>2 || pExpr->iDb!=0 ){ - sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited", - zDBase, pTab->zName, zCol); - }else{ - sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol); - } - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_OK ){ - sqliteAuthBadReturnCode(pParse, rc); - } -} - -/* -** Do an authorization check using the code and arguments given. Return -** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY -** is returned, then the error count and error message in pParse are -** modified appropriately. -*/ -int sqlite3AuthCheck( - Parse *pParse, - int code, - const char *zArg1, - const char *zArg2, - const char *zArg3 -){ - sqlite3 *db = pParse->db; - int rc; - - /* Don't do any authorization checks if the database is initialising. */ - if( db->init.busy ){ - return SQLITE_OK; - } - - if( db->xAuth==0 ){ - return SQLITE_OK; - } - rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext); - if( rc==SQLITE_DENY ){ - sqlite3ErrorMsg(pParse, "not authorized"); - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ - rc = SQLITE_DENY; - sqliteAuthBadReturnCode(pParse, rc); - } - return rc; -} - -/* -** Push an authorization context. After this routine is called, the -** zArg3 argument to authorization callbacks will be zContext until -** popped. Or if pParse==0, this routine is a no-op. -*/ -void sqlite3AuthContextPush( - Parse *pParse, - AuthContext *pContext, - const char *zContext -){ - pContext->pParse = pParse; - if( pParse ){ - pContext->zAuthContext = pParse->zAuthContext; - pParse->zAuthContext = zContext; - } -} - -/* -** Pop an authorization context that was previously pushed -** by sqlite3AuthContextPush -*/ -void sqlite3AuthContextPop(AuthContext *pContext){ - if( pContext->pParse ){ - pContext->pParse->zAuthContext = pContext->zAuthContext; - pContext->pParse = 0; - } -} - -#endif /* SQLITE_OMIT_AUTHORIZATION */ diff --git a/kopete/plugins/statistics/sqlite/btree.c b/kopete/plugins/statistics/sqlite/btree.c deleted file mode 100644 index fe8754e0..00000000 --- a/kopete/plugins/statistics/sqlite/btree.c +++ /dev/null @@ -1,4462 +0,0 @@ -/* -** 2004 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** $Id$ -** -** This file implements a external (disk-based) database using BTrees. -** For a detailed discussion of BTrees, refer to -** -** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: -** "Sorting And Searching", pages 473-480. Addison-Wesley -** Publishing Company, Reading, Massachusetts. -** -** The basic idea is that each page of the file contains N database -** entries and N+1 pointers to subpages. -** -** ---------------------------------------------------------------- -** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) | -** ---------------------------------------------------------------- -** -** All of the keys on the page that Ptr(0) points to have values less -** than Key(0). All of the keys on page Ptr(1) and its subpages have -** values greater than Key(0) and less than Key(1). All of the keys -** on Ptr(N+1) and its subpages have values greater than Key(N). And -** so forth. -** -** Finding a particular key requires reading O(log(M)) pages from the -** disk where M is the number of entries in the tree. -** -** In this implementation, a single file can hold one or more separate -** BTrees. Each BTree is identified by the index of its root page. The -** key and data for any entry are combined to form the "payload". A -** fixed amount of payload can be carried directly on the database -** page. If the payload is larger than the preset amount then surplus -** bytes are stored on overflow pages. The payload for an entry -** and the preceding pointer are combined to form a "Cell". Each -** page has a small header which contains the Ptr(N+1) pointer and other -** information such as the size of key and data. -** -** FORMAT DETAILS -** -** The file is divided into pages. The first page is called page 1, -** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be anything between 512 and 65536. -** Each page can be either a btree page, a freelist page or an overflow -** page. -** -** The first page is always a btree page. The first 100 bytes of the first -** page contain a special header (the "file header") that describes the file. -** The format of the file header is as follows: -** -** OFFSET SIZE DESCRIPTION -** 0 16 Header string: "SQLite format 3\000" -** 16 2 Page size in bytes. -** 18 1 File format write version -** 19 1 File format read version -** 20 1 Bytes of unused space at the end of each page -** 21 1 Max embedded payload fraction -** 22 1 Min embedded payload fraction -** 23 1 Min leaf payload fraction -** 24 4 File change counter -** 28 4 Reserved for future use -** 32 4 First freelist page -** 36 4 Number of freelist pages in the file -** 40 60 15 4-byte meta values passed to higher layers -** -** All of the integer values are big-endian (most significant byte first). -** -** The file change counter is incremented when the database is changed more -** than once within the same second. This counter, together with the -** modification time of the file, allows other processes to know -** when the file has changed and thus when they need to flush their -** cache. -** -** The max embedded payload fraction is the amount of the total usable -** space in a page that can be consumed by a single cell for standard -** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default -** is to limit the maximum cell size so that at least 4 cells will fit -** on one page. Thus the default max embedded payload fraction is 64. -** -** If the payload for a cell is larger than the max payload, then extra -** payload is spilled to overflow pages. Once an overflow page is allocated, -** as many bytes as possible are moved into the overflow pages without letting -** the cell size drop below the min embedded payload fraction. -** -** The min leaf payload fraction is like the min embedded payload fraction -** except that it applies to leaf nodes in a LEAFDATA tree. The maximum -** payload fraction for a LEAFDATA tree is always 100% (or 255) and it -** not specified in the header. -** -** Each btree pages is divided into three sections: The header, the -** cell pointer array, and the cell area area. Page 1 also has a 100-byte -** file header that occurs before the page header. -** -** |----------------| -** | file header | 100 bytes. Page 1 only. -** |----------------| -** | page header | 8 bytes for leaves. 12 bytes for interior nodes -** |----------------| -** | cell pointer | | 2 bytes per cell. Sorted order. -** | array | | Grows downward -** | | v -** |----------------| -** | unallocated | -** | space | -** |----------------| ^ Grows upwards -** | cell content | | Arbitrary order interspersed with freeblocks. -** | area | | and free space fragments. -** |----------------| -** -** The page headers looks like this: -** -** OFFSET SIZE DESCRIPTION -** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf -** 1 2 byte offset to the first freeblock -** 3 2 number of cells on this page -** 5 2 first byte of the cell content area -** 7 1 number of fragmented free bytes -** 8 4 Right child (the Ptr(N+1) value). Omitted on leaves. -** -** The flags define the format of this btree page. The leaf flag means that -** this page has no children. The zerodata flag means that this page carries -** only keys and no data. The intkey flag means that the key is a integer -** which is stored in the key size entry of the cell header rather than in -** the payload area. -** -** The cell pointer array begins on the first byte after the page header. -** The cell pointer array contains zero or more 2-byte numbers which are -** offsets from the beginning of the page to the cell content in the cell -** content area. The cell pointers occur in sorted order. The system strives -** to keep free space after the last cell pointer so that new cells can -** be easily added without having to defragment the page. -** -** Cell content is stored at the very end of the page and grows toward the -** beginning of the page. -** -** Unused space within the cell content area is collected into a linked list of -** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset -** to the first freeblock is given in the header. Freeblocks occur in -** increasing order. Because a freeblock must be at least 4 bytes in size, -** any group of 3 or fewer unused bytes in the cell content area cannot -** exist on the freeblock chain. A group of 3 or fewer free bytes is called -** a fragment. The total number of bytes in all fragments is recorded. -** in the page header at offset 7. -** -** SIZE DESCRIPTION -** 2 Byte offset of the next freeblock -** 2 Bytes in this freeblock -** -** Cells are of variable length. Cells are stored in the cell content area at -** the end of the page. Pointers to the cells are in the cell pointer array -** that immediately follows the page header. Cells is not necessarily -** contiguous or in order, but cell pointers are contiguous and in order. -** -** Cell content makes use of variable length integers. A variable -** length integer is 1 to 9 bytes where the lower 7 bits of each -** byte are used. The integer consists of all bytes that have bit 8 set and -** the first byte with bit 8 clear. The most significant byte of the integer -** appears first. A variable-length integer may not be more than 9 bytes long. -** As a special case, all 8 bytes of the 9th byte are used as data. This -** allows a 64-bit integer to be encoded in 9 bytes. -** -** 0x00 becomes 0x00000000 -** 0x7f becomes 0x0000007f -** 0x81 0x00 becomes 0x00000080 -** 0x82 0x00 becomes 0x00000100 -** 0x80 0x7f becomes 0x0000007f -** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 -** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 -** -** Variable length integers are used for rowids and to hold the number of -** bytes of key and data in a btree cell. -** -** The content of a cell looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of the left child. Omitted if leaf flag is set. -** var Number of bytes of data. Omitted if the zerodata flag is set. -** var Number of bytes of key. Or the key itself if intkey flag is set. -** * Payload -** 4 First page of the overflow chain. Omitted if no overflow -** -** Overflow pages form a linked list. Each page except the last is completely -** filled with data (pagesize - 4 bytes). The last page can have as little -** as 1 byte of data. -** -** SIZE DESCRIPTION -** 4 Page number of next overflow page -** * Data -** -** Freelist pages come in two subtypes: trunk pages and leaf pages. The -** file header points to first in a linked list of trunk page. Each trunk -** page points to multiple leaf pages. The content of a leaf page is -** unspecified. A trunk page looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of next trunk page -** 4 Number of leaf pointers on this page -** * zero or more pages numbers of leaves -*/ -#include "sqliteInt.h" -#include "pager.h" -#include "btree.h" -#include "os.h" -#include <assert.h> - - -/* The following value is the maximum cell size assuming a maximum page -** size give above. -*/ -#define MX_CELL_SIZE(pBt) (pBt->pageSize-8) - -/* The maximum number of cells on a single page of the database. This -** assumes a minimum cell size of 3 bytes. Such small cells will be -** exceedingly rare, but they are possible. -*/ -#define MX_CELL(pBt) ((pBt->pageSize-8)/3) - -/* Forward declarations */ -typedef struct MemPage MemPage; - -/* -** This is a magic string that appears at the beginning of every -** SQLite database in order to identify the file as a real database. -** 123456789 123456 */ -static const char zMagicHeader[] = "SQLite format 3"; - -/* -** Page type flags. An ORed combination of these flags appear as the -** first byte of every BTree page. -*/ -#define PTF_INTKEY 0x01 -#define PTF_ZERODATA 0x02 -#define PTF_LEAFDATA 0x04 -#define PTF_LEAF 0x08 - -/* -** As each page of the file is loaded into memory, an instance of the following -** structure is appended and initialized to zero. This structure stores -** information about the page that is decoded from the raw file page. -** -** The pParent field points back to the parent page. This allows us to -** walk up the BTree from any leaf to the root. Care must be taken to -** unref() the parent page pointer when this page is no longer referenced. -** The pageDestructor() routine handles that chore. -*/ -struct MemPage { - u8 isInit; /* True if previously initialized. MUST BE FIRST! */ - u8 idxShift; /* True if Cell indices have changed */ - u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ - u8 intKey; /* True if intkey flag is set */ - u8 leaf; /* True if leaf flag is set */ - u8 zeroData; /* True if table stores keys only */ - u8 leafData; /* True if tables stores data on leaves only */ - u8 hasData; /* True if this page stores data */ - u8 hdrOffset; /* 100 for page 1. 0 otherwise */ - u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ - u16 maxLocal; /* Copy of Btree.maxLocal or Btree.maxLeaf */ - u16 minLocal; /* Copy of Btree.minLocal or Btree.minLeaf */ - u16 cellOffset; /* Index in aData of first cell pointer */ - u16 idxParent; /* Index in parent of this node */ - u16 nFree; /* Number of free bytes on the page */ - u16 nCell; /* Number of cells on this page, local and ovfl */ - struct _OvflCell { /* Cells that will not fit on aData[] */ - u8 *pCell; /* Pointers to the body of the overflow cell */ - u16 idx; /* Insert this cell before idx-th non-overflow cell */ - } aOvfl[5]; - struct Btree *pBt; /* Pointer back to BTree structure */ - u8 *aData; /* Pointer back to the start of the page */ - Pgno pgno; /* Page number for this page */ - MemPage *pParent; /* The parent of this page. NULL for root */ -}; - -/* -** The in-memory image of a disk page has the auxiliary information appended -** to the end. EXTRA_SIZE is the number of bytes of space needed to hold -** that extra information. -*/ -#define EXTRA_SIZE sizeof(MemPage) - -/* -** Everything we need to know about an open database -*/ -struct Btree { - Pager *pPager; /* The page cache */ - BtCursor *pCursor; /* A list of all open cursors */ - MemPage *pPage1; /* First page of the database */ - u8 inTrans; /* True if a transaction is in progress */ - u8 inStmt; /* True if we are in a statement subtransaction */ - u8 readOnly; /* True if the underlying file is readonly */ - u8 maxEmbedFrac; /* Maximum payload as % of total page size */ - u8 minEmbedFrac; /* Minimum payload as % of total page size */ - u8 minLeafFrac; /* Minimum leaf payload as % of total page size */ - u8 pageSizeFixed; /* True if the page size can no longer be changed */ - u16 pageSize; /* Total number of bytes on a page */ - u16 usableSize; /* Number of usable bytes on each page */ - int maxLocal; /* Maximum local payload in non-LEAFDATA tables */ - int minLocal; /* Minimum local payload in non-LEAFDATA tables */ - int maxLeaf; /* Maximum local payload in a LEAFDATA table */ - int minLeaf; /* Minimum local payload in a LEAFDATA table */ -}; -typedef Btree Bt; - -/* -** Btree.inTrans may take one of the following values. -*/ -#define TRANS_NONE 0 -#define TRANS_READ 1 -#define TRANS_WRITE 2 - -/* -** An instance of the following structure is used to hold information -** about a cell. The parseCellPtr() function fills in this structure -** based on information extract from the raw disk page. -*/ -typedef struct CellInfo CellInfo; -struct CellInfo { - u8 *pCell; /* Pointer to the start of cell content */ - i64 nKey; /* The key for INTKEY tables, or number of bytes in key */ - u32 nData; /* Number of bytes of data */ - u16 nHeader; /* Size of the cell content header in bytes */ - u16 nLocal; /* Amount of payload held locally */ - u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */ - u16 nSize; /* Size of the cell content on the main b-tree page */ -}; - -/* -** A cursor is a pointer to a particular entry in the BTree. -** The entry is identified by its MemPage and the index in -** MemPage.aCell[] of the entry. -*/ -struct BtCursor { - Btree *pBt; /* The Btree to which this cursor belongs */ - BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ - int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */ - void *pArg; /* First arg to xCompare() */ - Pgno pgnoRoot; /* The root page of this tree */ - MemPage *pPage; /* Page that contains the entry */ - int idx; /* Index of the entry in pPage->aCell[] */ - CellInfo info; /* A parse of the cell we are pointing at */ - u8 wrFlag; /* True if writable */ - u8 isValid; /* TRUE if points to a valid entry */ - u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */ -}; - -/* -** Forward declaration -*/ -static int checkReadLocks(Btree*,Pgno,BtCursor*); - - -/* -** Read or write a two- and four-byte big-endian integer values. -*/ -static u32 get2byte(unsigned char *p){ - return (p[0]<<8) | p[1]; -} -static u32 get4byte(unsigned char *p){ - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; -} -static void put2byte(unsigned char *p, u32 v){ - p[0] = v>>8; - p[1] = v; -} -static void put4byte(unsigned char *p, u32 v){ - p[0] = v>>24; - p[1] = v>>16; - p[2] = v>>8; - p[3] = v; -} - -/* -** Routines to read and write variable-length integers. These used to -** be defined locally, but now we use the varint routines in the util.c -** file. -*/ -#define getVarint sqlite3GetVarint -#define getVarint32 sqlite3GetVarint32 -#define putVarint sqlite3PutVarint - -/* -** Given a btree page and a cell index (0 means the first cell on -** the page, 1 means the second cell, and so forth) return a pointer -** to the cell content. -** -** This routine works only for pages that do not contain overflow cells. -*/ -static u8 *findCell(MemPage *pPage, int iCell){ - u8 *data = pPage->aData; - assert( iCell>=0 ); - assert( iCell<get2byte(&data[pPage->hdrOffset+3]) ); - return data + get2byte(&data[pPage->cellOffset+2*iCell]); -} - -/* -** This a more complex version of findCell() that works for -** pages that do contain overflow cells. See insert -*/ -static u8 *findOverflowCell(MemPage *pPage, int iCell){ - int i; - for(i=pPage->nOverflow-1; i>=0; i--){ - int k; - struct _OvflCell *pOvfl; - pOvfl = &pPage->aOvfl[i]; - k = pOvfl->idx; - if( k<=iCell ){ - if( k==iCell ){ - return pOvfl->pCell; - } - iCell--; - } - } - return findCell(pPage, iCell); -} - -/* -** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. parseCell() takes a cell index -** as the second argument and parseCellPtr() takes a pointer to the -** body of the cell as its second argument. -*/ -static void parseCellPtr( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - int n; /* Number bytes in cell content header */ - u32 nPayload; /* Number of bytes of cell payload */ - - pInfo->pCell = pCell; - assert( pPage->leaf==0 || pPage->leaf==1 ); - n = pPage->childPtrSize; - assert( n==4-4*pPage->leaf ); - if( pPage->hasData ){ - n += getVarint32(&pCell[n], &nPayload); - }else{ - nPayload = 0; - } - n += getVarint(&pCell[n], (u64 *)&pInfo->nKey); - pInfo->nHeader = n; - pInfo->nData = nPayload; - if( !pPage->intKey ){ - nPayload += pInfo->nKey; - } - if( nPayload<=pPage->maxLocal ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ - int nSize; /* Total size of cell content in bytes */ - pInfo->nLocal = nPayload; - pInfo->iOverflow = 0; - nSize = nPayload + n; - if( nSize<4 ){ - nSize = 4; /* Minimum cell size is 4 */ - } - pInfo->nSize = nSize; - }else{ - /* If the payload will not fit completely on the local page, we have - ** to decide how much to store locally and how much to spill onto - ** overflow pages. The strategy is to minimize the amount of unused - ** space on overflow pages while keeping the amount of local storage - ** in between minLocal and maxLocal. - ** - ** Warning: changing the way overflow payload is distributed in any - ** way will result in an incompatible file format. - */ - int minLocal; /* Minimum amount of payload held locally */ - int maxLocal; /* Maximum amount of payload held locally */ - int surplus; /* Overflow payload available for local storage */ - - minLocal = pPage->minLocal; - maxLocal = pPage->maxLocal; - surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); - if( surplus <= maxLocal ){ - pInfo->nLocal = surplus; - }else{ - pInfo->nLocal = minLocal; - } - pInfo->iOverflow = pInfo->nLocal + n; - pInfo->nSize = pInfo->iOverflow + 4; - } -} -static void parseCell( - MemPage *pPage, /* Page containing the cell */ - int iCell, /* The cell index. First cell is 0 */ - CellInfo *pInfo /* Fill in this structure */ -){ - parseCellPtr(pPage, findCell(pPage, iCell), pInfo); -} - -/* -** Compute the total number of bytes that a Cell needs in the cell -** data area of the btree-page. The return number includes the cell -** data header and the local payload, but not any overflow page or -** the space used by the cell pointer. -*/ -#ifndef NDEBUG -static int cellSize(MemPage *pPage, int iCell){ - CellInfo info; - parseCell(pPage, iCell, &info); - return info.nSize; -} -#endif -static int cellSizePtr(MemPage *pPage, u8 *pCell){ - CellInfo info; - parseCellPtr(pPage, pCell, &info); - return info.nSize; -} - -/* -** Do sanity checking on a page. Throw an exception if anything is -** not right. -** -** This routine is used for internal error checking only. It is omitted -** from most builds. -*/ -#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0 -static void _pageIntegrity(MemPage *pPage){ - int usableSize; - u8 *data; - int i, j, idx, c, pc, hdr, nFree; - int cellOffset; - int nCell, cellLimit; - u8 *used; - - used = sqliteMallocRaw( pPage->pBt->pageSize ); - if( used==0 ) return; - usableSize = pPage->pBt->usableSize; - assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] ); - hdr = pPage->hdrOffset; - assert( hdr==(pPage->pgno==1 ? 100 : 0) ); - assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); - c = pPage->aData[hdr]; - if( pPage->isInit ){ - assert( pPage->leaf == ((c & PTF_LEAF)!=0) ); - assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) ); - assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) ); - assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) ); - assert( pPage->hasData == - !(pPage->zeroData || (!pPage->leaf && pPage->leafData)) ); - assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf ); - assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) ); - } - data = pPage->aData; - memset(used, 0, usableSize); - for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1; - nFree = 0; - pc = get2byte(&data[hdr+1]); - while( pc ){ - int size; - assert( pc>0 && pc<usableSize-4 ); - size = get2byte(&data[pc+2]); - assert( pc+size<=usableSize ); - nFree += size; - for(i=pc; i<pc+size; i++){ - assert( used[i]==0 ); - used[i] = 1; - } - pc = get2byte(&data[pc]); - } - idx = 0; - nCell = get2byte(&data[hdr+3]); - cellLimit = get2byte(&data[hdr+5]); - assert( pPage->isInit==0 - || pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) ); - cellOffset = pPage->cellOffset; - for(i=0; i<nCell; i++){ - int size; - pc = get2byte(&data[cellOffset+2*i]); - assert( pc>0 && pc<usableSize-4 ); - size = cellSize(pPage, &data[pc]); - assert( pc+size<=usableSize ); - for(j=pc; j<pc+size; j++){ - assert( used[j]==0 ); - used[j] = 1; - } - } - for(i=cellOffset+2*nCell; i<cellimit; i++){ - assert( used[i]==0 ); - used[i] = 1; - } - nFree = 0; - for(i=0; i<usableSize; i++){ - assert( used[i]<=1 ); - if( used[i]==0 ) nFree++; - } - assert( nFree==data[hdr+7] ); - sqliteFree(used); -} -#define pageIntegrity(X) _pageIntegrity(X) -#else -# define pageIntegrity(X) -#endif - -/* -** Defragment the page given. All Cells are moved to the -** beginning of the page and all free space is collected -** into one big FreeBlk at the end of the page. -*/ -static int defragmentPage(MemPage *pPage){ - int i; /* Loop counter */ - int pc; /* Address of a i-th cell */ - int addr; /* Offset of first byte after cell pointer array */ - int hdr; /* Offset to the page header */ - int size; /* Size of a cell */ - int usableSize; /* Number of usable bytes on a page */ - int cellOffset; /* Offset to the cell pointer array */ - int brk; /* Offset to the cell content area */ - int nCell; /* Number of cells on the page */ - unsigned char *data; /* The page data */ - unsigned char *temp; /* Temp area for cell content */ - - assert( sqlite3pager_iswriteable(pPage->aData) ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); - assert( pPage->nOverflow==0 ); - temp = sqliteMalloc( pPage->pBt->pageSize ); - if( temp==0 ) return SQLITE_NOMEM; - data = pPage->aData; - hdr = pPage->hdrOffset; - cellOffset = pPage->cellOffset; - nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) ); - usableSize = pPage->pBt->usableSize; - brk = get2byte(&data[hdr+5]); - memcpy(&temp[brk], &data[brk], usableSize - brk); - brk = usableSize; - for(i=0; i<nCell; i++){ - u8 *pAddr; /* The i-th cell pointer */ - pAddr = &data[cellOffset + i*2]; - pc = get2byte(pAddr); - assert( pc<pPage->pBt->usableSize ); - size = cellSizePtr(pPage, &temp[pc]); - brk -= size; - memcpy(&data[brk], &temp[pc], size); - put2byte(pAddr, brk); - } - assert( brk>=cellOffset+2*nCell ); - put2byte(&data[hdr+5], brk); - data[hdr+1] = 0; - data[hdr+2] = 0; - data[hdr+7] = 0; - addr = cellOffset+2*nCell; - memset(&data[addr], 0, brk-addr); - sqliteFree(temp); - return SQLITE_OK; -} - -/* -** Allocate nByte bytes of space on a page. -** -** Return the index into pPage->aData[] of the first byte of -** the new allocation. Or return 0 if there is not enough free -** space on the page to satisfy the allocation request. -** -** If the page contains nBytes of free space but does not contain -** nBytes of contiguous free space, then this routine automatically -** calls defragementPage() to consolidate all free space before -** allocating the new chunk. -*/ -static int allocateSpace(MemPage *pPage, int nByte){ - int addr, pc, hdr; - int size; - int nFrag; - int top; - int nCell; - int cellOffset; - unsigned char *data; - - data = pPage->aData; - assert( sqlite3pager_iswriteable(data) ); - assert( pPage->pBt ); - if( nByte<4 ) nByte = 4; - if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0; - pPage->nFree -= nByte; - hdr = pPage->hdrOffset; - - nFrag = data[hdr+7]; - if( nFrag<60 ){ - /* Search the freelist looking for a slot big enough to satisfy the - ** space request. */ - addr = hdr+1; - while( (pc = get2byte(&data[addr]))>0 ){ - size = get2byte(&data[pc+2]); - if( size>=nByte ){ - if( size<nByte+4 ){ - memcpy(&data[addr], &data[pc], 2); - data[hdr+7] = nFrag + size - nByte; - return pc; - }else{ - put2byte(&data[pc+2], size-nByte); - return pc + size - nByte; - } - } - addr = pc; - } - } - - /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. - */ - top = get2byte(&data[hdr+5]); - nCell = get2byte(&data[hdr+3]); - cellOffset = pPage->cellOffset; - if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){ - if( defragmentPage(pPage) ) return 0; - top = get2byte(&data[hdr+5]); - } - top -= nByte; - assert( cellOffset + 2*nCell <= top ); - put2byte(&data[hdr+5], top); - return top; -} - -/* -** Return a section of the pPage->aData to the freelist. -** The first byte of the new free block is pPage->aDisk[start] -** and the size of the block is "size" bytes. -** -** Most of the effort here is involved in coalesing adjacent -** free blocks into a single big free block. -*/ -static void freeSpace(MemPage *pPage, int start, int size){ - int addr, pbegin, hdr; - unsigned char *data = pPage->aData; - - assert( pPage->pBt!=0 ); - assert( sqlite3pager_iswriteable(data) ); - assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) ); - assert( (start + size)<=pPage->pBt->usableSize ); - if( size<4 ) size = 4; - - /* Add the space back into the linked list of freeblocks */ - hdr = pPage->hdrOffset; - addr = hdr + 1; - while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){ - assert( pbegin<=pPage->pBt->usableSize-4 ); - assert( pbegin>addr ); - addr = pbegin; - } - assert( pbegin<=pPage->pBt->usableSize-4 ); - assert( pbegin>addr || pbegin==0 ); - put2byte(&data[addr], start); - put2byte(&data[start], pbegin); - put2byte(&data[start+2], size); - pPage->nFree += size; - - /* Coalesce adjacent free blocks */ - addr = pPage->hdrOffset + 1; - while( (pbegin = get2byte(&data[addr]))>0 ){ - int pnext, psize; - assert( pbegin>addr ); - assert( pbegin<=pPage->pBt->usableSize-4 ); - pnext = get2byte(&data[pbegin]); - psize = get2byte(&data[pbegin+2]); - if( pbegin + psize + 3 >= pnext && pnext>0 ){ - int frag = pnext - (pbegin+psize); - assert( frag<=data[pPage->hdrOffset+7] ); - data[pPage->hdrOffset+7] -= frag; - put2byte(&data[pbegin], get2byte(&data[pnext])); - put2byte(&data[pbegin+2], pnext+get2byte(&data[pnext+2])-pbegin); - }else{ - addr = pbegin; - } - } - - /* If the cell content area begins with a freeblock, remove it. */ - if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){ - int top; - pbegin = get2byte(&data[hdr+1]); - memcpy(&data[hdr+1], &data[pbegin], 2); - top = get2byte(&data[hdr+5]); - put2byte(&data[hdr+5], top + get2byte(&data[pbegin+2])); - } -} - -/* -** Decode the flags byte (the first byte of the header) for a page -** and initialize fields of the MemPage structure accordingly. -*/ -static void decodeFlags(MemPage *pPage, int flagByte){ - Btree *pBt; /* A copy of pPage->pBt */ - - assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); - pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0; - pPage->zeroData = (flagByte & PTF_ZERODATA)!=0; - pPage->leaf = (flagByte & PTF_LEAF)!=0; - pPage->childPtrSize = 4*(pPage->leaf==0); - pBt = pPage->pBt; - if( flagByte & PTF_LEAFDATA ){ - pPage->leafData = 1; - pPage->maxLocal = pBt->maxLeaf; - pPage->minLocal = pBt->minLeaf; - }else{ - pPage->leafData = 0; - pPage->maxLocal = pBt->maxLocal; - pPage->minLocal = pBt->minLocal; - } - pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData)); -} - -/* -** Initialize the auxiliary information for a disk block. -** -** The pParent parameter must be a pointer to the MemPage which -** is the parent of the page being initialized. The root of a -** BTree has no parent and so for that page, pParent==NULL. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. -*/ -static int initPage( - MemPage *pPage, /* The page to be initialized */ - MemPage *pParent /* The parent. Might be NULL */ -){ - int pc; /* Address of a freeblock within pPage->aData[] */ - int i; /* Loop counter */ - int hdr; /* Offset to beginning of page header */ - u8 *data; /* Equal to pPage->aData */ - Btree *pBt; /* The main btree structure */ - int usableSize; /* Amount of usable space on each page */ - int cellOffset; /* Offset from start of page to first cell pointer */ - int nFree; /* Number of unused bytes on the page */ - int top; /* First byte of the cell content area */ - - pBt = pPage->pBt; - assert( pBt!=0 ); - assert( pParent==0 || pParent->pBt==pBt ); - assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); - assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] ); - if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){ - /* The parent page should never change unless the file is corrupt */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - if( pPage->isInit ) return SQLITE_OK; - if( pPage->pParent==0 && pParent!=0 ){ - pPage->pParent = pParent; - sqlite3pager_ref(pParent->aData); - } - hdr = pPage->hdrOffset; - data = pPage->aData; - decodeFlags(pPage, data[hdr]); - pPage->nOverflow = 0; - pPage->idxShift = 0; - usableSize = pBt->usableSize; - pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; - top = get2byte(&data[hdr+5]); - pPage->nCell = get2byte(&data[hdr+3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - if( pPage->nCell==0 && pParent!=0 && pParent->pgno!=1 ){ - /* All pages must have at least one cell, except for root pages */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - - /* Compute the total free space on the page */ - pc = get2byte(&data[hdr+1]); - nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell); - i = 0; - while( pc>0 ){ - int next, size; - if( pc>usableSize-4 ){ - /* Free block is off the page */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - if( i++>SQLITE_MAX_PAGE_SIZE/4 ){ - /* The free block list forms an infinite loop */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - next = get2byte(&data[pc]); - size = get2byte(&data[pc+2]); - if( next>0 && next<=pc+size+3 ){ - /* Free blocks must be in accending order */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - nFree += size; - pc = next; - } - pPage->nFree = nFree; - if( nFree>=usableSize ){ - /* Free space cannot exceed total page size */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - - pPage->isInit = 1; - pageIntegrity(pPage); - return SQLITE_OK; -} - -/* -** Set up a raw page so that it looks like a database page holding -** no entries. -*/ -static void zeroPage(MemPage *pPage, int flags){ - unsigned char *data = pPage->aData; - Btree *pBt = pPage->pBt; - int hdr = pPage->hdrOffset; - int first; - - assert( sqlite3pager_pagenumber(data)==pPage->pgno ); - assert( &data[pBt->pageSize] == (unsigned char*)pPage ); - assert( sqlite3pager_iswriteable(data) ); - memset(&data[hdr], 0, pBt->usableSize - hdr); - data[hdr] = flags; - first = hdr + 8 + 4*((flags&PTF_LEAF)==0); - memset(&data[hdr+1], 0, 4); - data[hdr+7] = 0; - put2byte(&data[hdr+5], pBt->usableSize); - pPage->nFree = pBt->usableSize - first; - decodeFlags(pPage, flags); - pPage->hdrOffset = hdr; - pPage->cellOffset = first; - pPage->nOverflow = 0; - pPage->idxShift = 0; - pPage->nCell = 0; - pPage->isInit = 1; - pageIntegrity(pPage); -} - -/* -** Get a page from the pager. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -*/ -static int getPage(Btree *pBt, Pgno pgno, MemPage **ppPage){ - int rc; - unsigned char *aData; - MemPage *pPage; - rc = sqlite3pager_get(pBt->pPager, pgno, (void**)&aData); - if( rc ) return rc; - pPage = (MemPage*)&aData[pBt->pageSize]; - pPage->aData = aData; - pPage->pBt = pBt; - pPage->pgno = pgno; - pPage->hdrOffset = pPage->pgno==1 ? 100 : 0; - *ppPage = pPage; - return SQLITE_OK; -} - -/* -** Get a page from the pager and initialize it. This routine -** is just a convenience wrapper around separate calls to -** getPage() and initPage(). -*/ -static int getAndInitPage( - Btree *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage, /* Write the page pointer here */ - MemPage *pParent /* Parent of the page */ -){ - int rc; - if( pgno==0 ){ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - rc = getPage(pBt, pgno, ppPage); - if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){ - rc = initPage(*ppPage, pParent); - } - return rc; -} - -/* -** Release a MemPage. This should be called once for each prior -** call to getPage. -*/ -static void releasePage(MemPage *pPage){ - if( pPage ){ - assert( pPage->aData ); - assert( pPage->pBt ); - assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage ); - sqlite3pager_unref(pPage->aData); - } -} - -/* -** This routine is called when the reference count for a page -** reaches zero. We need to unref the pParent pointer when that -** happens. -*/ -static void pageDestructor(void *pData, int pageSize){ - MemPage *pPage = (MemPage*)&((char*)pData)[pageSize]; - if( pPage->pParent ){ - MemPage *pParent = pPage->pParent; - pPage->pParent = 0; - releasePage(pParent); - } - pPage->isInit = 0; -} - -/* -** During a rollback, when the pager reloads information into the cache -** so that the cache is restored to its original state at the start of -** the transaction, for each page restored this routine is called. -** -** This routine needs to reset the extra data section at the end of the -** page to agree with the restored data. -*/ -static void pageReinit(void *pData, int pageSize){ - MemPage *pPage = (MemPage*)&((char*)pData)[pageSize]; - if( pPage->isInit ){ - pPage->isInit = 0; - initPage(pPage, pPage->pParent); - } -} - -/* -** Open a database file. -** -** zFilename is the name of the database file. If zFilename is NULL -** a new database with a random name is created. This randomly named -** database file will be deleted when sqlite3BtreeClose() is called. -*/ -int sqlite3BtreeOpen( - const char *zFilename, /* Name of the file containing the BTree database */ - Btree **ppBtree, /* Pointer to new Btree object written here */ - int flags /* Options */ -){ - Btree *pBt; - int rc; - int nReserve; - unsigned char zDbHeader[100]; - - /* - ** The following asserts make sure that structures used by the btree are - ** the right size. This is to guard against size changes that result - ** when compiling on a different architecture. - */ - assert( sizeof(i64)==8 ); - assert( sizeof(u64)==8 ); - assert( sizeof(u32)==4 ); - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); - assert( sizeof(ptr)==sizeof(char*) ); - assert( sizeof(uptr)==sizeof(ptr) ); - - pBt = sqliteMalloc( sizeof(*pBt) ); - if( pBt==0 ){ - *ppBtree = 0; - return SQLITE_NOMEM; - } - rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE, - (flags & BTREE_OMIT_JOURNAL)==0); - if( rc!=SQLITE_OK ){ - if( pBt->pPager ) sqlite3pager_close(pBt->pPager); - sqliteFree(pBt); - *ppBtree = 0; - return rc; - } - sqlite3pager_set_destructor(pBt->pPager, pageDestructor); - sqlite3pager_set_reiniter(pBt->pPager, pageReinit); - pBt->pCursor = 0; - pBt->pPage1 = 0; - pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager); - sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader); - pBt->pageSize = get2byte(&zDbHeader[16]); - if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE ){ - pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE; - pBt->maxEmbedFrac = 64; /* 25% */ - pBt->minEmbedFrac = 32; /* 12.5% */ - pBt->minLeafFrac = 32; /* 12.5% */ - nReserve = 0; - }else{ - nReserve = zDbHeader[20]; - pBt->maxEmbedFrac = zDbHeader[21]; - pBt->minEmbedFrac = zDbHeader[22]; - pBt->minLeafFrac = zDbHeader[23]; - pBt->pageSizeFixed = 1; - } - pBt->usableSize = pBt->pageSize - nReserve; - sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize); - *ppBtree = pBt; - return SQLITE_OK; -} - -/* -** Close an open database and invalidate all cursors. -*/ -int sqlite3BtreeClose(Btree *pBt){ - while( pBt->pCursor ){ - sqlite3BtreeCloseCursor(pBt->pCursor); - } - sqlite3pager_close(pBt->pPager); - sqliteFree(pBt); - return SQLITE_OK; -} - -/* -** Change the busy handler callback function. -*/ -int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){ - sqlite3pager_set_busyhandler(pBt->pPager, pHandler); - return SQLITE_OK; -} - -/* -** Change the limit on the number of pages allowed in the cache. -** -** The maximum number of cache pages is set to the absolute -** value of mxPage. If mxPage is negative, the pager will -** operate asynchronously - it will not stop to do fsync()s -** to insure data is written to the disk surface before -** continuing. Transactions still work if synchronous is off, -** and the database cannot be corrupted if this program -** crashes. But if the operating system crashes or there is -** an abrupt power failure when synchronous is off, the database -** could be left in an inconsistent and unrecoverable state. -** Synchronous is on by default so database corruption is not -** normally a worry. -*/ -int sqlite3BtreeSetCacheSize(Btree *pBt, int mxPage){ - sqlite3pager_set_cachesize(pBt->pPager, mxPage); - return SQLITE_OK; -} - -/* -** Change the way data is synced to disk in order to increase or decrease -** how well the database resists damage due to OS crashes and power -** failures. Level 1 is the same as asynchronous (no syncs() occur and -** there is a high probability of damage) Level 2 is the default. There -** is a very low but non-zero probability of damage. Level 3 reduces the -** probability of damage to near zero but with a write performance reduction. -*/ -int sqlite3BtreeSetSafetyLevel(Btree *pBt, int level){ - sqlite3pager_set_safety_level(pBt->pPager, level); - return SQLITE_OK; -} - -/* -** Change the default pages size and the number of reserved bytes per page. -*/ -int sqlite3BtreeSetPageSize(Btree *pBt, int pageSize, int nReserve){ - if( pBt->pageSizeFixed ){ - return SQLITE_READONLY; - } - if( nReserve<0 ){ - nReserve = pBt->pageSize - pBt->usableSize; - } - if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ){ - pBt->pageSize = pageSize; - sqlite3pager_set_pagesize(pBt->pPager, pageSize); - } - pBt->usableSize = pBt->pageSize - nReserve; - return SQLITE_OK; -} - -/* -** Return the currently defined page size -*/ -int sqlite3BtreeGetPageSize(Btree *pBt){ - return pBt->pageSize; -} -int sqlite3BtreeGetReserve(Btree *pBt){ - return pBt->pageSize - pBt->usableSize; -} - -/* -** Get a reference to pPage1 of the database file. This will -** also acquire a readlock on that file. -** -** SQLITE_OK is returned on success. If the file is not a -** well-formed database file, then SQLITE_CORRUPT is returned. -** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM -** is returned if we run out of memory. SQLITE_PROTOCOL is returned -** if there is a locking protocol violation. -*/ -static int lockBtree(Btree *pBt){ - int rc; - MemPage *pPage1; - if( pBt->pPage1 ) return SQLITE_OK; - rc = getPage(pBt, 1, &pPage1); - if( rc!=SQLITE_OK ) return rc; - - - /* Do some checking to help insure the file we opened really is - ** a valid database file. - */ - rc = SQLITE_NOTADB; - if( sqlite3pager_pagecount(pBt->pPager)>0 ){ - u8 *page1 = pPage1->aData; - if( memcmp(page1, zMagicHeader, 16)!=0 ){ - goto page1_init_failed; - } - if( page1[18]>1 || page1[19]>1 ){ - goto page1_init_failed; - } - pBt->pageSize = get2byte(&page1[16]); - pBt->usableSize = pBt->pageSize - page1[20]; - if( pBt->usableSize<500 ){ - goto page1_init_failed; - } - pBt->maxEmbedFrac = page1[21]; - pBt->minEmbedFrac = page1[22]; - pBt->minLeafFrac = page1[23]; - } - - /* maxLocal is the maximum amount of payload to store locally for - ** a cell. Make sure it is small enough so that at least minFanout - ** cells can will fit on one page. We assume a 10-byte page header. - ** Besides the payload, the cell must store: - ** 2-byte pointer to the cell - ** 4-byte child pointer - ** 9-byte nKey value - ** 4-byte nData value - ** 4-byte overflow page pointer - ** So a cell consists of a 2-byte poiner, a header which is as much as - ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow - ** page pointer. - */ - pBt->maxLocal = (pBt->usableSize-12)*pBt->maxEmbedFrac/255 - 23; - pBt->minLocal = (pBt->usableSize-12)*pBt->minEmbedFrac/255 - 23; - pBt->maxLeaf = pBt->usableSize - 35; - pBt->minLeaf = (pBt->usableSize-12)*pBt->minLeafFrac/255 - 23; - if( pBt->minLocal>pBt->maxLocal || pBt->maxLocal<0 ){ - goto page1_init_failed; - } - assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); - pBt->pPage1 = pPage1; - return SQLITE_OK; - -page1_init_failed: - releasePage(pPage1); - pBt->pPage1 = 0; - return rc; -} - -/* -** If there are no outstanding cursors and we are not in the middle -** of a transaction but there is a read lock on the database, then -** this routine unrefs the first page of the database file which -** has the effect of releasing the read lock. -** -** If there are any outstanding cursors, this routine is a no-op. -** -** If there is a transaction in progress, this routine is a no-op. -*/ -static void unlockBtreeIfUnused(Btree *pBt){ - if( pBt->inTrans==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ - if( pBt->pPage1->aData==0 ){ - MemPage *pPage = pBt->pPage1; - pPage->aData = &((char*)pPage)[-pBt->pageSize]; - pPage->pBt = pBt; - pPage->pgno = 1; - } - releasePage(pBt->pPage1); - pBt->pPage1 = 0; - pBt->inStmt = 0; - } -} - -/* -** Create a new database by initializing the first page of the -** file. -*/ -static int newDatabase(Btree *pBt){ - MemPage *pP1; - unsigned char *data; - int rc; - if( sqlite3pager_pagecount(pBt->pPager)>0 ) return SQLITE_OK; - pP1 = pBt->pPage1; - assert( pP1!=0 ); - data = pP1->aData; - rc = sqlite3pager_write(data); - if( rc ) return rc; - memcpy(data, zMagicHeader, sizeof(zMagicHeader)); - assert( sizeof(zMagicHeader)==16 ); - put2byte(&data[16], pBt->pageSize); - data[18] = 1; - data[19] = 1; - data[20] = pBt->pageSize - pBt->usableSize; - data[21] = pBt->maxEmbedFrac; - data[22] = pBt->minEmbedFrac; - data[23] = pBt->minLeafFrac; - memset(&data[24], 0, 100-24); - zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); - pBt->pageSizeFixed = 1; - return SQLITE_OK; -} - -/* -** Attempt to start a new transaction. A write-transaction -** is started if the second argument is nonzero, otherwise a read- -** transaction. If the second argument is 2 or more and exclusive -** transaction is started, meaning that no other process is allowed -** to access the database. A preexisting transaction may not be -** upgrade to exclusive by calling this routine a second time - the -** exclusivity flag only works for a new transaction. -** -** A write-transaction must be started before attempting any -** changes to the database. None of the following routines -** will work unless a transaction is started first: -** -** sqlite3BtreeCreateTable() -** sqlite3BtreeCreateIndex() -** sqlite3BtreeClearTable() -** sqlite3BtreeDropTable() -** sqlite3BtreeInsert() -** sqlite3BtreeDelete() -** sqlite3BtreeUpdateMeta() -** -** If wrflag is true, then nMaster specifies the maximum length of -** a master journal file name supplied later via sqlite3BtreeSync(). -** This is so that appropriate space can be allocated in the journal file -** when it is created.. -*/ -int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){ - int rc = SQLITE_OK; - - /* If the btree is already in a write-transaction, or it - ** is already in a read-transaction and a read-transaction - ** is requested, this is a no-op. - */ - if( pBt->inTrans==TRANS_WRITE || - (pBt->inTrans==TRANS_READ && !wrflag) ){ - return SQLITE_OK; - } - if( pBt->readOnly && wrflag ){ - return SQLITE_READONLY; - } - - if( pBt->pPage1==0 ){ - rc = lockBtree(pBt); - } - - if( rc==SQLITE_OK && wrflag ){ - rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1); - if( rc==SQLITE_OK ){ - rc = newDatabase(pBt); - } - } - - if( rc==SQLITE_OK ){ - pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); - if( wrflag ) pBt->inStmt = 0; - }else{ - unlockBtreeIfUnused(pBt); - } - return rc; -} - -/* -** Commit the transaction currently in progress. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -int sqlite3BtreeCommit(Btree *pBt){ - int rc = SQLITE_OK; - if( pBt->inTrans==TRANS_WRITE ){ - rc = sqlite3pager_commit(pBt->pPager); - } - pBt->inTrans = TRANS_NONE; - pBt->inStmt = 0; - unlockBtreeIfUnused(pBt); - return rc; -} - -#ifndef NDEBUG -/* -** Return the number of write-cursors open on this handle. This is for use -** in assert() expressions, so it is only compiled if NDEBUG is not -** defined. -*/ -static int countWriteCursors(Btree *pBt){ - BtCursor *pCur; - int r = 0; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->wrFlag ) r++; - } - return r; -} -#endif - -#if 0 -/* -** Invalidate all cursors -*/ -static void invalidateCursors(Btree *pBt){ - BtCursor *pCur; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - MemPage *pPage = pCur->pPage; - if( pPage /* && !pPage->isInit */ ){ - pageIntegrity(pPage); - releasePage(pPage); - pCur->pPage = 0; - pCur->isValid = 0; - pCur->status = SQLITE_ABORT; - } - } -} -#endif - -#ifdef SQLITE_TEST -/* -** Print debugging information about all cursors to standard output. -*/ -void sqlite3BtreeCursorList(Btree *pBt){ - BtCursor *pCur; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - MemPage *pPage = pCur->pPage; - char *zMode = pCur->wrFlag ? "rw" : "ro"; - sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", - pCur, pCur->pgnoRoot, zMode, - pPage ? pPage->pgno : 0, pCur->idx, - pCur->isValid ? "" : " eof" - ); - } -} -#endif - -/* -** Rollback the transaction in progress. All cursors will be -** invalided by this operation. Any attempt to use a cursor -** that was open at the beginning of this operation will result -** in an error. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -int sqlite3BtreeRollback(Btree *pBt){ - int rc = SQLITE_OK; - MemPage *pPage1; - if( pBt->inTrans==TRANS_WRITE ){ - rc = sqlite3pager_rollback(pBt->pPager); - /* The rollback may have destroyed the pPage1->aData value. So - ** call getPage() on page 1 again to make sure pPage1->aData is - ** set correctly. */ - if( getPage(pBt, 1, &pPage1)==SQLITE_OK ){ - releasePage(pPage1); - } - assert( countWriteCursors(pBt)==0 ); - } - pBt->inTrans = TRANS_NONE; - pBt->inStmt = 0; - unlockBtreeIfUnused(pBt); - return rc; -} - -/* -** Start a statement subtransaction. The subtransaction can -** can be rolled back independently of the main transaction. -** You must start a transaction before starting a subtransaction. -** The subtransaction is ended automatically if the main transaction -** commits or rolls back. -** -** Only one subtransaction may be active at a time. It is an error to try -** to start a new subtransaction if another subtransaction is already active. -** -** Statement subtransactions are used around individual SQL statements -** that are contained within a BEGIN...COMMIT block. If a constraint -** error occurs within the statement, the effect of that one statement -** can be rolled back without having to rollback the entire transaction. -*/ -int sqlite3BtreeBeginStmt(Btree *pBt){ - int rc; - if( (pBt->inTrans!=TRANS_WRITE) || pBt->inStmt ){ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_stmt_begin(pBt->pPager); - pBt->inStmt = 1; - return rc; -} - - -/* -** Commit the statment subtransaction currently in progress. If no -** subtransaction is active, this is a no-op. -*/ -int sqlite3BtreeCommitStmt(Btree *pBt){ - int rc; - if( pBt->inStmt && !pBt->readOnly ){ - rc = sqlite3pager_stmt_commit(pBt->pPager); - }else{ - rc = SQLITE_OK; - } - pBt->inStmt = 0; - return rc; -} - -/* -** Rollback the active statement subtransaction. If no subtransaction -** is active this routine is a no-op. -** -** All cursors will be invalidated by this operation. Any attempt -** to use a cursor that was open at the beginning of this operation -** will result in an error. -*/ -int sqlite3BtreeRollbackStmt(Btree *pBt){ - int rc; - if( pBt->inStmt==0 || pBt->readOnly ) return SQLITE_OK; - rc = sqlite3pager_stmt_rollback(pBt->pPager); - assert( countWriteCursors(pBt)==0 ); - pBt->inStmt = 0; - return rc; -} - -/* -** Default key comparison function to be used if no comparison function -** is specified on the sqlite3BtreeCursor() call. -*/ -static int dfltCompare( - void *NotUsed, /* User data is not used */ - int n1, const void *p1, /* First key to compare */ - int n2, const void *p2 /* Second key to compare */ -){ - int c; - c = memcmp(p1, p2, n1<n2 ? n1 : n2); - if( c==0 ){ - c = n1 - n2; - } - return c; -} - -/* -** Create a new cursor for the BTree whose root is on the page -** iTable. The act of acquiring a cursor gets a read lock on -** the database file. -** -** If wrFlag==0, then the cursor can only be used for reading. -** If wrFlag==1, then the cursor can be used for reading or for -** writing if other conditions for writing are also met. These -** are the conditions that must be met in order for writing to -** be allowed: -** -** 1: The cursor must have been opened with wrFlag==1 -** -** 2: No other cursors may be open with wrFlag==0 on the same table -** -** 3: The database must be writable (not on read-only media) -** -** 4: There must be an active transaction. -** -** Condition 2 warrants further discussion. If any cursor is opened -** on a table with wrFlag==0, that prevents all other cursors from -** writing to that table. This is a kind of "read-lock". When a cursor -** is opened with wrFlag==0 it is guaranteed that the table will not -** change as long as the cursor is open. This allows the cursor to -** do a sequential scan of the table without having to worry about -** entries being inserted or deleted during the scan. Cursors should -** be opened with wrFlag==0 only if this read-lock property is needed. -** That is to say, cursors should be opened with wrFlag==0 only if they -** intend to use the sqlite3BtreeNext() system call. All other cursors -** should be opened with wrFlag==1 even if they never really intend -** to write. -** -** No checking is done to make sure that page iTable really is the -** root page of a b-tree. If it is not, then the cursor acquired -** will not work correctly. -** -** The comparison function must be logically the same for every cursor -** on a particular table. Changing the comparison function will result -** in incorrect operations. If the comparison function is NULL, a -** default comparison function is used. The comparison function is -** always ignored for INTKEY tables. -*/ -int sqlite3BtreeCursor( - Btree *pBt, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */ - void *pArg, /* First arg to xCompare() */ - BtCursor **ppCur /* Write new cursor here */ -){ - int rc; - BtCursor *pCur; - - *ppCur = 0; - if( wrFlag ){ - if( pBt->readOnly ){ - return SQLITE_READONLY; - } - if( checkReadLocks(pBt, iTable, 0) ){ - return SQLITE_LOCKED; - } - } - if( pBt->pPage1==0 ){ - rc = lockBtree(pBt); - if( rc!=SQLITE_OK ){ - return rc; - } - } - pCur = sqliteMallocRaw( sizeof(*pCur) ); - if( pCur==0 ){ - rc = SQLITE_NOMEM; - goto create_cursor_exception; - } - pCur->pgnoRoot = (Pgno)iTable; - if( iTable==1 && sqlite3pager_pagecount(pBt->pPager)==0 ){ - rc = SQLITE_EMPTY; - pCur->pPage = 0; - goto create_cursor_exception; - } - pCur->pPage = 0; /* For exit-handler, in case getAndInitPage() fails. */ - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->pPage, 0); - if( rc!=SQLITE_OK ){ - goto create_cursor_exception; - } - pCur->xCompare = xCmp ? xCmp : dfltCompare; - pCur->pArg = pArg; - pCur->pBt = pBt; - pCur->wrFlag = wrFlag; - pCur->idx = 0; - memset(&pCur->info, 0, sizeof(pCur->info)); - pCur->pNext = pBt->pCursor; - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur; - } - pCur->pPrev = 0; - pBt->pCursor = pCur; - pCur->isValid = 0; - pCur->status = SQLITE_OK; - *ppCur = pCur; - return SQLITE_OK; - -create_cursor_exception: - if( pCur ){ - releasePage(pCur->pPage); - sqliteFree(pCur); - } - unlockBtreeIfUnused(pBt); - return rc; -} - -#if 0 /* Not Used */ -/* -** Change the value of the comparison function used by a cursor. -*/ -void sqlite3BtreeSetCompare( - BtCursor *pCur, /* The cursor to whose comparison function is changed */ - int(*xCmp)(void*,int,const void*,int,const void*), /* New comparison func */ - void *pArg /* First argument to xCmp() */ -){ - pCur->xCompare = xCmp ? xCmp : dfltCompare; - pCur->pArg = pArg; -} -#endif - -/* -** Close a cursor. The read lock on the database file is released -** when the last cursor is closed. -*/ -int sqlite3BtreeCloseCursor(BtCursor *pCur){ - Btree *pBt = pCur->pBt; - if( pCur->pPrev ){ - pCur->pPrev->pNext = pCur->pNext; - }else{ - pBt->pCursor = pCur->pNext; - } - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur->pPrev; - } - releasePage(pCur->pPage); - unlockBtreeIfUnused(pBt); - sqliteFree(pCur); - return SQLITE_OK; -} - -/* -** Make a temporary cursor by filling in the fields of pTempCur. -** The temporary cursor is not on the cursor list for the Btree. -*/ -static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ - memcpy(pTempCur, pCur, sizeof(*pCur)); - pTempCur->pNext = 0; - pTempCur->pPrev = 0; - if( pTempCur->pPage ){ - sqlite3pager_ref(pTempCur->pPage->aData); - } -} - -/* -** Delete a temporary cursor such as was made by the CreateTemporaryCursor() -** function above. -*/ -static void releaseTempCursor(BtCursor *pCur){ - if( pCur->pPage ){ - sqlite3pager_unref(pCur->pPage->aData); - } -} - -/* -** Make sure the BtCursor.info field of the given cursor is valid. -** If it is not already valid, call parseCell() to fill it in. -** -** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to parseCell(). -*/ -static void getCellInfo(BtCursor *pCur){ - if( pCur->info.nSize==0 ){ - parseCell(pCur->pPage, pCur->idx, &pCur->info); - }else{ -#ifndef NDEBUG - CellInfo info; - memset(&info, 0, sizeof(info)); - parseCell(pCur->pPage, pCur->idx, &info); - assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); -#endif - } -} - -/* -** Set *pSize to the size of the buffer needed to hold the value of -** the key for the current entry. If the cursor is not pointing -** to a valid entry, *pSize is set to 0. -** -** For a table with the INTKEY flag set, this routine returns the key -** itself, not the number of bytes in the key. -*/ -int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ - if( !pCur->isValid ){ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nKey; - } - return SQLITE_OK; -} - -/* -** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. Always return SQLITE_OK. -** Failure is not possible. If the cursor is not currently -** pointing to an entry (which can happen, for example, if -** the database is empty) then *pSize is set to 0. -*/ -int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - if( !pCur->isValid ){ - /* Not pointing at a valid entry - set *pSize to 0. */ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nData; - } - return SQLITE_OK; -} - -/* -** Read payload information from the entry that the pCur cursor is -** pointing to. Begin reading the payload at "offset" and read -** a total of "amt" bytes. Put the result in zBuf. -** -** This routine does not make a distinction between key and data. -** It just reads bytes from the payload area. Data might appear -** on the main page or be scattered out on multiple overflow pages. -*/ -static int getPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - int offset, /* Begin reading this far into payload */ - int amt, /* Read this many bytes */ - unsigned char *pBuf, /* Write the bytes into this buffer */ - int skipKey /* offset begins at data if this is true */ -){ - unsigned char *aPayload; - Pgno nextPage; - int rc; - MemPage *pPage; - Btree *pBt; - int ovflSize; - u32 nKey; - - assert( pCur!=0 && pCur->pPage!=0 ); - assert( pCur->isValid ); - pBt = pCur->pBt; - pPage = pCur->pPage; - pageIntegrity(pPage); - assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); - getCellInfo(pCur); - aPayload = pCur->info.pCell; - aPayload += pCur->info.nHeader; - if( pPage->intKey ){ - nKey = 0; - }else{ - nKey = pCur->info.nKey; - } - assert( offset>=0 ); - if( skipKey ){ - offset += nKey; - } - if( offset+amt > nKey+pCur->info.nData ){ - return SQLITE_ERROR; - } - if( offset<pCur->info.nLocal ){ - int a = amt; - if( a+offset>pCur->info.nLocal ){ - a = pCur->info.nLocal - offset; - } - memcpy(pBuf, &aPayload[offset], a); - if( a==amt ){ - return SQLITE_OK; - } - offset = 0; - pBuf += a; - amt -= a; - }else{ - offset -= pCur->info.nLocal; - } - ovflSize = pBt->usableSize - 4; - if( amt>0 ){ - nextPage = get4byte(&aPayload[pCur->info.nLocal]); - while( amt>0 && nextPage ){ - rc = sqlite3pager_get(pBt->pPager, nextPage, (void**)&aPayload); - if( rc!=0 ){ - return rc; - } - nextPage = get4byte(aPayload); - if( offset<ovflSize ){ - int a = amt; - if( a + offset > ovflSize ){ - a = ovflSize - offset; - } - memcpy(pBuf, &aPayload[offset+4], a); - offset = 0; - amt -= a; - pBuf += a; - }else{ - offset -= ovflSize; - } - sqlite3pager_unref(aPayload); - } - } - - if( amt>0 ){ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - return SQLITE_OK; -} - -/* -** Read part of the key associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - if( pCur->isValid==0 ){ - return pCur->status; - } - assert( pCur->pPage!=0 ); - assert( pCur->pPage->intKey==0 ); - assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); - return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); -} - -/* -** Read part of the data associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - if( !pCur->isValid ){ - return pCur->status ? pCur->status : SQLITE_INTERNAL; - } - assert( pCur->pPage!=0 ); - assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); - return getPayload(pCur, offset, amt, pBuf, 1); -} - -/* -** Return a pointer to payload information from the entry that the -** pCur cursor is pointing to. The pointer is to the beginning of -** the key if skipKey==0 and it points to the beginning of data if -** skipKey==1. The number of bytes of available key/data is written -** into *pAmt. If *pAmt==0, then the value returned will not be -** a valid pointer. -** -** This routine is an optimization. It is common for the entire key -** and data to fit on the local page and for there to be no overflow -** pages. When that is so, this routine can be used to access the -** key and data without making a copy. If the key and/or data spills -** onto overflow pages, then getPayload() must be used to reassembly -** the key/data and copy it into a preallocated buffer. -** -** The pointer returned by this routine looks directly into the cached -** page of the database. The data might change or move the next time -** any btree routine is called. -*/ -static const unsigned char *fetchPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - int *pAmt, /* Write the number of available bytes here */ - int skipKey /* read beginning at data if this is true */ -){ - unsigned char *aPayload; - MemPage *pPage; - Btree *pBt; - u32 nKey; - int nLocal; - - assert( pCur!=0 && pCur->pPage!=0 ); - assert( pCur->isValid ); - pBt = pCur->pBt; - pPage = pCur->pPage; - pageIntegrity(pPage); - assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); - getCellInfo(pCur); - aPayload = pCur->info.pCell; - aPayload += pCur->info.nHeader; - if( pPage->intKey ){ - nKey = 0; - }else{ - nKey = pCur->info.nKey; - } - if( skipKey ){ - aPayload += nKey; - nLocal = pCur->info.nLocal - nKey; - }else{ - nLocal = pCur->info.nLocal; - if( nLocal>nKey ){ - nLocal = nKey; - } - } - *pAmt = nLocal; - return aPayload; -} - - -/* -** For the entry that cursor pCur is point to, return as -** many bytes of the key or data as are available on the local -** b-tree page. Write the number of available bytes into *pAmt. -** -** The pointer returned is ephemeral. The key/data may move -** or be destroyed on the next call to any Btree routine. -** -** These routines is used to get quick access to key and data -** in the common case where no overflow pages are used. -*/ -const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ - return (const void*)fetchPayload(pCur, pAmt, 0); -} -const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ - return (const void*)fetchPayload(pCur, pAmt, 1); -} - - -/* -** Move the cursor down to a new child page. The newPgno argument is the -** page number of the child page to move to. -*/ -static int moveToChild(BtCursor *pCur, u32 newPgno){ - int rc; - MemPage *pNewPage; - MemPage *pOldPage; - Btree *pBt = pCur->pBt; - - assert( pCur->isValid ); - rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); - if( rc ) return rc; - pageIntegrity(pNewPage); - pNewPage->idxParent = pCur->idx; - pOldPage = pCur->pPage; - pOldPage->idxShift = 0; - releasePage(pOldPage); - pCur->pPage = pNewPage; - pCur->idx = 0; - pCur->info.nSize = 0; - if( pNewPage->nCell<1 ){ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - return SQLITE_OK; -} - -/* -** Return true if the page is the virtual root of its table. -** -** The virtual root page is the root page for most tables. But -** for the table rooted on page 1, sometime the real root page -** is empty except for the right-pointer. In such cases the -** virtual root page is the page that the right-pointer of page -** 1 is pointing to. -*/ -static int isRootPage(MemPage *pPage){ - MemPage *pParent = pPage->pParent; - if( pParent==0 ) return 1; - if( pParent->pgno>1 ) return 0; - if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1; - return 0; -} - -/* -** Move the cursor up to the parent page. -** -** pCur->idx is set to the cell index that contains the pointer -** to the page we are coming from. If we are coming from the -** right-most child page then pCur->idx is set to one more than -** the largest cell index. -*/ -static void moveToParent(BtCursor *pCur){ - Pgno oldPgno; - MemPage *pParent; - MemPage *pPage; - int idxParent; - - assert( pCur->isValid ); - pPage = pCur->pPage; - assert( pPage!=0 ); - assert( !isRootPage(pPage) ); - pageIntegrity(pPage); - pParent = pPage->pParent; - assert( pParent!=0 ); - pageIntegrity(pParent); - idxParent = pPage->idxParent; - sqlite3pager_ref(pParent->aData); - oldPgno = pPage->pgno; - releasePage(pPage); - pCur->pPage = pParent; - pCur->info.nSize = 0; - assert( pParent->idxShift==0 ); - pCur->idx = idxParent; -} - -/* -** Move the cursor to the root page -*/ -static int moveToRoot(BtCursor *pCur){ - MemPage *pRoot; - int rc; - Btree *pBt = pCur->pBt; - - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0); - if( rc ){ - pCur->isValid = 0; - return rc; - } - releasePage(pCur->pPage); - pageIntegrity(pRoot); - pCur->pPage = pRoot; - pCur->idx = 0; - pCur->info.nSize = 0; - if( pRoot->nCell==0 && !pRoot->leaf ){ - Pgno subpage; - assert( pRoot->pgno==1 ); - subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - assert( subpage>0 ); - pCur->isValid = 1; - rc = moveToChild(pCur, subpage); - } - pCur->isValid = pCur->pPage->nCell>0; - return rc; -} - -/* -** Move the cursor down to the left-most leaf entry beneath the -** entry to which it is currently pointing. -*/ -static int moveToLeftmost(BtCursor *pCur){ - Pgno pgno; - int rc; - MemPage *pPage; - - assert( pCur->isValid ); - while( !(pPage = pCur->pPage)->leaf ){ - assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); - pgno = get4byte(findCell(pPage, pCur->idx)); - rc = moveToChild(pCur, pgno); - if( rc ) return rc; - } - return SQLITE_OK; -} - -/* -** Move the cursor down to the right-most leaf entry beneath the -** page to which it is currently pointing. Notice the difference -** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() -** finds the left-most entry beneath the *entry* whereas moveToRightmost() -** finds the right-most entry beneath the *page*. -*/ -static int moveToRightmost(BtCursor *pCur){ - Pgno pgno; - int rc; - MemPage *pPage; - - assert( pCur->isValid ); - while( !(pPage = pCur->pPage)->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - pCur->idx = pPage->nCell; - rc = moveToChild(pCur, pgno); - if( rc ) return rc; - } - pCur->idx = pPage->nCell - 1; - pCur->info.nSize = 0; - return SQLITE_OK; -} - -/* Move the cursor to the first entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ - int rc; - if( pCur->status ){ - return pCur->status; - } - rc = moveToRoot(pCur); - if( rc ) return rc; - if( pCur->isValid==0 ){ - assert( pCur->pPage->nCell==0 ); - *pRes = 1; - return SQLITE_OK; - } - assert( pCur->pPage->nCell>0 ); - *pRes = 0; - rc = moveToLeftmost(pCur); - return rc; -} - -/* Move the cursor to the last entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - int rc; - if( pCur->status ){ - return pCur->status; - } - rc = moveToRoot(pCur); - if( rc ) return rc; - if( pCur->isValid==0 ){ - assert( pCur->pPage->nCell==0 ); - *pRes = 1; - return SQLITE_OK; - } - assert( pCur->isValid ); - *pRes = 0; - rc = moveToRightmost(pCur); - return rc; -} - -/* Move the cursor so that it points to an entry near pKey/nKey. -** Return a success code. -** -** For INTKEY tables, only the nKey parameter is used. pKey is -** ignored. For other tables, nKey is the number of bytes of data -** in nKey. The comparison function specified when the cursor was -** created is used to compare keys. -** -** If an exact match is not found, then the cursor is always -** left pointing at a leaf page which would hold the entry if it -** were present. The cursor might point to an entry that comes -** before or after the key. -** -** The result of comparing the key with the entry to which the -** cursor is written to *pRes if pRes!=NULL. The meaning of -** this value is as follows: -** -** *pRes<0 The cursor is left pointing at an entry that -** is smaller than pKey or if the table is empty -** and the cursor is therefore left point to nothing. -** -** *pRes==0 The cursor is left pointing at an entry that -** exactly matches pKey. -** -** *pRes>0 The cursor is left pointing at an entry that -** is larger than pKey. -*/ -int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){ - int rc; - - if( pCur->status ){ - return pCur->status; - } - rc = moveToRoot(pCur); - if( rc ) return rc; - assert( pCur->pPage ); - assert( pCur->pPage->isInit ); - if( pCur->isValid==0 ){ - *pRes = -1; - assert( pCur->pPage->nCell==0 ); - return SQLITE_OK; - } - for(;;){ - int lwr, upr; - Pgno chldPg; - MemPage *pPage = pCur->pPage; - int c = -1; /* pRes return if table is empty must be -1 */ - lwr = 0; - upr = pPage->nCell-1; - pageIntegrity(pPage); - while( lwr<=upr ){ - void *pCellKey; - i64 nCellKey; - pCur->idx = (lwr+upr)/2; - pCur->info.nSize = 0; - sqlite3BtreeKeySize(pCur, &nCellKey); - if( pPage->intKey ){ - if( nCellKey<nKey ){ - c = -1; - }else if( nCellKey>nKey ){ - c = +1; - }else{ - c = 0; - } - }else{ - int available; - pCellKey = (void *)fetchPayload(pCur, &available, 0); - if( available>=nCellKey ){ - c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); - }else{ - pCellKey = sqliteMallocRaw( nCellKey ); - if( pCellKey==0 ) return SQLITE_NOMEM; - rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey); - c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); - sqliteFree(pCellKey); - if( rc ) return rc; - } - } - if( c==0 ){ - if( pPage->leafData && !pPage->leaf ){ - lwr = pCur->idx; - upr = lwr - 1; - break; - }else{ - if( pRes ) *pRes = 0; - return SQLITE_OK; - } - } - if( c<0 ){ - lwr = pCur->idx+1; - }else{ - upr = pCur->idx-1; - } - } - assert( lwr==upr+1 ); - assert( pPage->isInit ); - if( pPage->leaf ){ - chldPg = 0; - }else if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - if( chldPg==0 ){ - assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); - if( pRes ) *pRes = c; - return SQLITE_OK; - } - pCur->idx = lwr; - pCur->info.nSize = 0; - rc = moveToChild(pCur, chldPg); - if( rc ){ - return rc; - } - } - /* NOT REACHED */ -} - -/* -** Return TRUE if the cursor is not pointing at an entry of the table. -** -** TRUE will be returned after a call to sqlite3BtreeNext() moves -** past the last entry in the table or sqlite3BtreePrev() moves past -** the first entry. TRUE is also returned if the table is empty. -*/ -int sqlite3BtreeEof(BtCursor *pCur){ - return pCur->isValid==0; -} - -/* -** Advance the cursor to the next entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the last entry in the database before -** this routine was called, then set *pRes=1. -*/ -int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ - int rc; - MemPage *pPage = pCur->pPage; - - assert( pRes!=0 ); - if( pCur->isValid==0 ){ - *pRes = 1; - return SQLITE_OK; - } - assert( pPage->isInit ); - assert( pCur->idx<pPage->nCell ); - pCur->idx++; - pCur->info.nSize = 0; - if( pCur->idx>=pPage->nCell ){ - if( !pPage->leaf ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - if( rc ) return rc; - rc = moveToLeftmost(pCur); - *pRes = 0; - return rc; - } - do{ - if( isRootPage(pPage) ){ - *pRes = 1; - pCur->isValid = 0; - return SQLITE_OK; - } - moveToParent(pCur); - pPage = pCur->pPage; - }while( pCur->idx>=pPage->nCell ); - *pRes = 0; - if( pPage->leafData ){ - rc = sqlite3BtreeNext(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - return rc; - } - *pRes = 0; - if( pPage->leaf ){ - return SQLITE_OK; - } - rc = moveToLeftmost(pCur); - return rc; -} - -/* -** Step the cursor to the back to the previous entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the first entry in the database before -** this routine was called, then set *pRes=1. -*/ -int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ - int rc; - Pgno pgno; - MemPage *pPage; - if( pCur->isValid==0 ){ - *pRes = 1; - return SQLITE_OK; - } - pPage = pCur->pPage; - assert( pPage->isInit ); - assert( pCur->idx>=0 ); - if( !pPage->leaf ){ - pgno = get4byte( findCell(pPage, pCur->idx) ); - rc = moveToChild(pCur, pgno); - if( rc ) return rc; - rc = moveToRightmost(pCur); - }else{ - while( pCur->idx==0 ){ - if( isRootPage(pPage) ){ - pCur->isValid = 0; - *pRes = 1; - return SQLITE_OK; - } - moveToParent(pCur); - pPage = pCur->pPage; - } - pCur->idx--; - pCur->info.nSize = 0; - if( pPage->leafData ){ - rc = sqlite3BtreePrevious(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - } - *pRes = 0; - return rc; -} - -/* -** The TRACE macro will print high-level status information about the -** btree operation when the global variable sqlite3_btree_trace is -** enabled. -*/ -#if SQLITE_TEST -# define TRACE(X) if( sqlite3_btree_trace )\ - { sqlite3DebugPrintf X; fflush(stdout); } -#else -# define TRACE(X) -#endif -int sqlite3_btree_trace=0; /* True to enable tracing */ - -/* -** Allocate a new page from the database file. -** -** The new page is marked as dirty. (In other words, sqlite3pager_write() -** has already been called on the new page.) The new page has also -** been referenced and the calling routine is responsible for calling -** sqlite3pager_unref() on the new page when it is done. -** -** SQLITE_OK is returned on success. Any other return value indicates -** an error. *ppPage and *pPgno are undefined in the event of an error. -** Do not invoke sqlite3pager_unref() on *ppPage if an error is returned. -** -** If the "nearby" parameter is not 0, then a (feeble) effort is made to -** locate a page close to the page number "nearby". This can be used in an -** attempt to keep related pages close to each other in the database file, -** which in turn can make database access faster. -*/ -static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){ - MemPage *pPage1; - int rc; - int n; /* Number of pages on the freelist */ - int k; /* Number of leaves on the trunk of the freelist */ - - pPage1 = pBt->pPage1; - n = get4byte(&pPage1->aData[36]); - if( n>0 ){ - /* There are pages on the freelist. Reuse one of those pages. */ - MemPage *pTrunk; - rc = sqlite3pager_write(pPage1->aData); - if( rc ) return rc; - put4byte(&pPage1->aData[36], n-1); - rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk); - if( rc ) return rc; - rc = sqlite3pager_write(pTrunk->aData); - if( rc ){ - releasePage(pTrunk); - return rc; - } - k = get4byte(&pTrunk->aData[4]); - if( k==0 ){ - /* The trunk has no leaves. So extract the trunk page itself and - ** use it as the newly allocated page */ - *pPgno = get4byte(&pPage1->aData[32]); - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - *ppPage = pTrunk; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); - }else if( k>pBt->usableSize/4 - 8 ){ - /* Value of k is out of range. Database corruption */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - }else{ - /* Extract a leaf from the trunk */ - int closest; - unsigned char *aData = pTrunk->aData; - if( nearby>0 ){ - int i, dist; - closest = 0; - dist = get4byte(&aData[8]) - nearby; - if( dist<0 ) dist = -dist; - for(i=1; i<k; i++){ - int d2 = get4byte(&aData[8+i*4]) - nearby; - if( d2<0 ) d2 = -d2; - if( d2<dist ) closest = i; - } - }else{ - closest = 0; - } - *pPgno = get4byte(&aData[8+closest*4]); - if( *pPgno>sqlite3pager_pagecount(pBt->pPager) ){ - /* Free page off the end of the file */ - return SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d: %d more free pages\n", - *pPgno, closest+1, k, pTrunk->pgno, n-1)); - if( closest<k-1 ){ - memcpy(&aData[8+closest*4], &aData[4+k*4], 4); - } - put4byte(&aData[4], k-1); - rc = getPage(pBt, *pPgno, ppPage); - releasePage(pTrunk); - if( rc==SQLITE_OK ){ - sqlite3pager_dont_rollback((*ppPage)->aData); - rc = sqlite3pager_write((*ppPage)->aData); - } - } - }else{ - /* There are no pages on the freelist, so create a new page at the - ** end of the file */ - *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1; - rc = getPage(pBt, *pPgno, ppPage); - if( rc ) return rc; - rc = sqlite3pager_write((*ppPage)->aData); - TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); - } - return rc; -} - -/* -** Add a page of the database file to the freelist. -** -** sqlite3pager_unref() is NOT called for pPage. -*/ -static int freePage(MemPage *pPage){ - Btree *pBt = pPage->pBt; - MemPage *pPage1 = pBt->pPage1; - int rc, n, k; - - /* Prepare the page for freeing */ - assert( pPage->pgno>1 ); - pPage->isInit = 0; - releasePage(pPage->pParent); - pPage->pParent = 0; - - /* Increment the free page count on pPage1 */ - rc = sqlite3pager_write(pPage1->aData); - if( rc ) return rc; - n = get4byte(&pPage1->aData[36]); - put4byte(&pPage1->aData[36], n+1); - - if( n==0 ){ - /* This is the first free page */ - rc = sqlite3pager_write(pPage->aData); - if( rc ) return rc; - memset(pPage->aData, 0, 8); - put4byte(&pPage1->aData[32], pPage->pgno); - TRACE(("FREE-PAGE: %d first\n", pPage->pgno)); - }else{ - /* Other free pages already exist. Retrive the first trunk page - ** of the freelist and find out how many leaves it has. */ - MemPage *pTrunk; - rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk); - if( rc ) return rc; - k = get4byte(&pTrunk->aData[4]); - if( k>=pBt->usableSize/4 - 8 ){ - /* The trunk is full. Turn the page being freed into a new - ** trunk page with no leaves. */ - rc = sqlite3pager_write(pPage->aData); - if( rc ) return rc; - put4byte(pPage->aData, pTrunk->pgno); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], pPage->pgno); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", - pPage->pgno, pTrunk->pgno)); - }else{ - /* Add the newly freed page as a leaf on the current trunk */ - rc = sqlite3pager_write(pTrunk->aData); - if( rc ) return rc; - put4byte(&pTrunk->aData[4], k+1); - put4byte(&pTrunk->aData[8+k*4], pPage->pgno); - sqlite3pager_dont_write(pBt->pPager, pPage->pgno); - TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); - } - releasePage(pTrunk); - } - return rc; -} - -/* -** Free any overflow pages associated with the given Cell. -*/ -static int clearCell(MemPage *pPage, unsigned char *pCell){ - Btree *pBt = pPage->pBt; - CellInfo info; - Pgno ovflPgno; - int rc; - - parseCellPtr(pPage, pCell, &info); - if( info.iOverflow==0 ){ - return SQLITE_OK; /* No overflow pages. Return without doing anything */ - } - ovflPgno = get4byte(&pCell[info.iOverflow]); - while( ovflPgno!=0 ){ - MemPage *pOvfl; - rc = getPage(pBt, ovflPgno, &pOvfl); - if( rc ) return rc; - ovflPgno = get4byte(pOvfl->aData); - rc = freePage(pOvfl); - if( rc ) return rc; - sqlite3pager_unref(pOvfl->aData); - } - return SQLITE_OK; -} - -/* -** Create the byte sequence used to represent a cell on page pPage -** and write that byte sequence into pCell[]. Overflow pages are -** allocated and filled in as necessary. The calling procedure -** is responsible for making sure sufficient space has been allocated -** for pCell[]. -** -** Note that pCell does not necessary need to point to the pPage->aData -** area. pCell might point to some temporary storage. The cell will -** be constructed in this temporary area then copied into pPage->aData -** later. -*/ -static int fillInCell( - MemPage *pPage, /* The page that contains the cell */ - unsigned char *pCell, /* Complete text of the cell */ - const void *pKey, i64 nKey, /* The key */ - const void *pData,int nData, /* The data */ - int *pnSize /* Write cell size here */ -){ - int nPayload; - const u8 *pSrc; - int nSrc, n, rc; - int spaceLeft; - MemPage *pOvfl = 0; - MemPage *pToRelease = 0; - unsigned char *pPrior; - unsigned char *pPayload; - Btree *pBt = pPage->pBt; - Pgno pgnoOvfl = 0; - int nHeader; - CellInfo info; - - /* Fill in the header. */ - nHeader = 0; - if( !pPage->leaf ){ - nHeader += 4; - } - if( pPage->hasData ){ - nHeader += putVarint(&pCell[nHeader], nData); - }else{ - nData = 0; - } - nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - parseCellPtr(pPage, pCell, &info); - assert( info.nHeader==nHeader ); - assert( info.nKey==nKey ); - assert( info.nData==nData ); - - /* Fill in the payload */ - nPayload = nData; - if( pPage->intKey ){ - pSrc = pData; - nSrc = nData; - nData = 0; - }else{ - nPayload += nKey; - pSrc = pKey; - nSrc = nKey; - } - *pnSize = info.nSize; - spaceLeft = info.nLocal; - pPayload = &pCell[nHeader]; - pPrior = &pCell[info.iOverflow]; - - while( nPayload>0 ){ - if( spaceLeft==0 ){ - rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl); - if( rc ){ - releasePage(pToRelease); - clearCell(pPage, pCell); - return rc; - } - put4byte(pPrior, pgnoOvfl); - releasePage(pToRelease); - pToRelease = pOvfl; - pPrior = pOvfl->aData; - put4byte(pPrior, 0); - pPayload = &pOvfl->aData[4]; - spaceLeft = pBt->usableSize - 4; - } - n = nPayload; - if( n>spaceLeft ) n = spaceLeft; - if( n>nSrc ) n = nSrc; - memcpy(pPayload, pSrc, n); - nPayload -= n; - pPayload += n; - pSrc += n; - nSrc -= n; - spaceLeft -= n; - if( nSrc==0 ){ - nSrc = nData; - pSrc = pData; - } - } - releasePage(pToRelease); - return SQLITE_OK; -} - -/* -** Change the MemPage.pParent pointer on the page whose number is -** given in the second argument so that MemPage.pParent holds the -** pointer in the third argument. -*/ -static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){ - MemPage *pThis; - unsigned char *aData; - - if( pgno==0 ) return; - assert( pBt->pPager!=0 ); - aData = sqlite3pager_lookup(pBt->pPager, pgno); - if( aData ){ - pThis = (MemPage*)&aData[pBt->pageSize]; - assert( pThis->aData==aData ); - if( pThis->isInit ){ - if( pThis->pParent!=pNewParent ){ - if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); - pThis->pParent = pNewParent; - if( pNewParent ) sqlite3pager_ref(pNewParent->aData); - } - pThis->idxParent = idx; - } - sqlite3pager_unref(aData); - } -} - -/* -** Change the pParent pointer of all children of pPage to point back -** to pPage. -** -** In other words, for every child of pPage, invoke reparentPage() -** to make sure that each child knows that pPage is its parent. -** -** This routine gets called after you memcpy() one page into -** another. -*/ -static void reparentChildPages(MemPage *pPage){ - int i; - Btree *pBt; - - if( pPage->leaf ) return; - pBt = pPage->pBt; - for(i=0; i<pPage->nCell; i++){ - reparentPage(pBt, get4byte(findCell(pPage,i)), pPage, i); - } - reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]), pPage, i); - pPage->idxShift = 0; -} - -/* -** Remove the i-th cell from pPage. This routine effects pPage only. -** The cell content is not freed or deallocated. It is assumed that -** the cell content has been copied someplace else. This routine just -** removes the reference to the cell from pPage. -** -** "sz" must be the number of bytes in the cell. -*/ -static void dropCell(MemPage *pPage, int idx, int sz){ - int i; /* Loop counter */ - int pc; /* Offset to cell content of cell being deleted */ - u8 *data; /* pPage->aData */ - u8 *ptr; /* Used to move bytes around within data[] */ - - assert( idx>=0 && idx<pPage->nCell ); - assert( sz==cellSize(pPage, idx) ); - assert( sqlite3pager_iswriteable(pPage->aData) ); - data = pPage->aData; - ptr = &data[pPage->cellOffset + 2*idx]; - pc = get2byte(ptr); - assert( pc>10 && pc+sz<=pPage->pBt->usableSize ); - freeSpace(pPage, pc, sz); - for(i=idx+1; i<pPage->nCell; i++, ptr+=2){ - ptr[0] = ptr[2]; - ptr[1] = ptr[3]; - } - pPage->nCell--; - put2byte(&data[pPage->hdrOffset+3], pPage->nCell); - pPage->nFree += 2; - pPage->idxShift = 1; -} - -/* -** Insert a new cell on pPage at cell index "i". pCell points to the -** content of the cell. -** -** If the cell content will fit on the page, then put it there. If it -** will not fit, then make a copy of the cell content into pTemp if -** pTemp is not null. Regardless of pTemp, allocate a new entry -** in pPage->aOvfl[] and make it point to the cell content (either -** in pTemp or the original pCell) and also record its index. -** Allocating a new entry in pPage->aCell[] implies that -** pPage->nOverflow is incremented. -*/ -static void insertCell( - MemPage *pPage, /* Page into which we are copying */ - int i, /* New cell becomes the i-th cell of the page */ - u8 *pCell, /* Content of the new cell */ - int sz, /* Bytes of content in pCell */ - u8 *pTemp /* Temp storage space for pCell, if needed */ -){ - int idx; /* Where to write new cell content in data[] */ - int j; /* Loop counter */ - int top; /* First byte of content for any cell in data[] */ - int end; /* First byte past the last cell pointer in data[] */ - int ins; /* Index in data[] where new cell pointer is inserted */ - int hdr; /* Offset into data[] of the page header */ - int cellOffset; /* Address of first cell pointer in data[] */ - u8 *data; /* The content of the whole page */ - u8 *ptr; /* Used for moving information around in data[] */ - - assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); - assert( sz==cellSizePtr(pPage, pCell) ); - assert( sqlite3pager_iswriteable(pPage->aData) ); - if( pPage->nOverflow || sz+2>pPage->nFree ){ - if( pTemp ){ - memcpy(pTemp, pCell, sz); - pCell = pTemp; - } - j = pPage->nOverflow++; - assert( j<sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0]) ); - pPage->aOvfl[j].pCell = pCell; - pPage->aOvfl[j].idx = i; - pPage->nFree = 0; - }else{ - data = pPage->aData; - hdr = pPage->hdrOffset; - top = get2byte(&data[hdr+5]); - cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell + 2; - ins = cellOffset + 2*i; - if( end > top - sz ){ - defragmentPage(pPage); - top = get2byte(&data[hdr+5]); - assert( end + sz <= top ); - } - idx = allocateSpace(pPage, sz); - assert( idx>0 ); - assert( end <= get2byte(&data[hdr+5]) ); - pPage->nCell++; - pPage->nFree -= 2; - memcpy(&data[idx], pCell, sz); - for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){ - ptr[0] = ptr[-2]; - ptr[1] = ptr[-1]; - } - put2byte(&data[ins], idx); - put2byte(&data[hdr+3], pPage->nCell); - pPage->idxShift = 1; - pageIntegrity(pPage); - } -} - -/* -** Add a list of cells to a page. The page should be initially empty. -** The cells are guaranteed to fit on the page. -*/ -static void assemblePage( - MemPage *pPage, /* The page to be assemblied */ - int nCell, /* The number of cells to add to this page */ - u8 **apCell, /* Pointers to cell bodies */ - int *aSize /* Sizes of the cells */ -){ - int i; /* Loop counter */ - int totalSize; /* Total size of all cells */ - int hdr; /* Index of page header */ - int cellptr; /* Address of next cell pointer */ - int cellbody; /* Address of next cell body */ - u8 *data; /* Data for the page */ - - assert( pPage->nOverflow==0 ); - totalSize = 0; - for(i=0; i<nCell; i++){ - totalSize += aSize[i]; - } - assert( totalSize+2*nCell<=pPage->nFree ); - assert( pPage->nCell==0 ); - cellptr = pPage->cellOffset; - data = pPage->aData; - hdr = pPage->hdrOffset; - put2byte(&data[hdr+3], nCell); - cellbody = allocateSpace(pPage, totalSize); - assert( cellbody>0 ); - assert( pPage->nFree >= 2*nCell ); - pPage->nFree -= 2*nCell; - for(i=0; i<nCell; i++){ - put2byte(&data[cellptr], cellbody); - memcpy(&data[cellbody], apCell[i], aSize[i]); - cellptr += 2; - cellbody += aSize[i]; - } - assert( cellbody==pPage->pBt->usableSize ); - pPage->nCell = nCell; -} - -/* -** GCC does not define the offsetof() macro so we'll have to do it -** ourselves. -*/ -#ifndef offsetof -#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) -#endif - -/* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB (NN*2+1) /* Total pages involved in the balance */ - -/* Forward reference */ -static int balance(MemPage*); - -/* -** This routine redistributes Cells on pPage and up to NN*2 siblings -** of pPage so that all pages have about the same amount of free space. -** Usually NN siblings on either side of pPage is used in the balancing, -** though more siblings might come from one side if pPage is the first -** or last child of its parent. If pPage has fewer than 2*NN siblings -** (something which can only happen if pPage is the root page or a -** child of root) then all available siblings participate in the balancing. -** -** The number of siblings of pPage might be increased or decreased by one or -** two in an effort to keep pages nearly full but not over full. The root page -** is special and is allowed to be nearly empty. If pPage is -** the root page, then the depth of the tree might be increased -** or decreased by one, as necessary, to keep the root page from being -** overfull or completely empty. -** -** Note that when this routine is called, some of the Cells on pPage -** might not actually be stored in pPage->aData[]. This can happen -** if the page is overfull. Part of the job of this routine is to -** make sure all Cells for pPage once again fit in pPage->aData[]. -** -** In the course of balancing the siblings of pPage, the parent of pPage -** might become overfull or underfull. If that happens, then this routine -** is called recursively on the parent. -** -** If this routine fails for any reason, it might leave the database -** in a corrupted state. So if this routine fails, the database should -** be rolled back. -*/ -static int balance_nonroot(MemPage *pPage){ - MemPage *pParent; /* The parent of pPage */ - Btree *pBt; /* The whole database */ - int nCell = 0; /* Number of cells in aCell[] */ - int nOld; /* Number of pages in apOld[] */ - int nNew; /* Number of pages in apNew[] */ - int nDiv; /* Number of cells in apDiv[] */ - int i, j, k; /* Loop counters */ - int idx; /* Index of pPage in pParent->aCell[] */ - int nxDiv; /* Next divider slot in pParent->aCell[] */ - int rc; /* The return code */ - int leafCorrection; /* 4 if pPage is a leaf. 0 if not */ - int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ - int usableSpace; /* Bytes in pPage beyond the header */ - int pageFlags; /* Value of pPage->aData[0] */ - int subtotal; /* Subtotal of bytes in cells on one page */ - int iSpace = 0; /* First unused byte of aSpace[] */ - int mxCellPerPage; /* Maximum number of cells in one page */ - MemPage *apOld[NB]; /* pPage and up to two siblings */ - Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */ - MemPage *apCopy[NB]; /* Private copies of apOld[] pages */ - MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ - Pgno pgnoNew[NB+2]; /* Page numbers for each page in apNew[] */ - int idxDiv[NB]; /* Indices of divider cells in pParent */ - u8 *apDiv[NB]; /* Divider cells in pParent */ - int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */ - int szNew[NB+2]; /* Combined size of cells place on i-th page */ - u8 **apCell; /* All cells begin balanced */ - int *szCell; /* Local size of all cells in apCell[] */ - u8 *aCopy[NB]; /* Space for holding data of apCopy[] */ - u8 *aSpace; /* Space to hold copies of dividers cells */ - - /* - ** Find the parent page. - */ - assert( pPage->isInit ); - assert( sqlite3pager_iswriteable(pPage->aData) ); - pBt = pPage->pBt; - pParent = pPage->pParent; - sqlite3pager_write(pParent->aData); - assert( pParent ); - TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); - - /* - ** Allocate space for memory structures - */ - mxCellPerPage = MX_CELL(pBt); - apCell = sqliteMallocRaw( - (mxCellPerPage+2)*NB*(sizeof(u8*)+sizeof(int)) - + sizeof(MemPage)*NB - + pBt->pageSize*(5+NB) - ); - if( apCell==0 ){ - return SQLITE_NOMEM; - } - szCell = (int*)&apCell[(mxCellPerPage+2)*NB]; - aCopy[0] = (u8*)&szCell[(mxCellPerPage+2)*NB]; - for(i=1; i<NB; i++){ - aCopy[i] = &aCopy[i-1][pBt->pageSize+sizeof(MemPage)]; - } - aSpace = &aCopy[NB-1][pBt->pageSize+sizeof(MemPage)]; - - /* - ** Find the cell in the parent page whose left child points back - ** to pPage. The "idx" variable is the index of that cell. If pPage - ** is the rightmost child of pParent then set idx to pParent->nCell - */ - if( pParent->idxShift ){ - Pgno pgno; - pgno = pPage->pgno; - assert( pgno==sqlite3pager_pagenumber(pPage->aData) ); - for(idx=0; idx<pParent->nCell; idx++){ - if( get4byte(findCell(pParent, idx))==pgno ){ - break; - } - } - assert( idx<pParent->nCell - || get4byte(&pParent->aData[pParent->hdrOffset+8])==pgno ); - }else{ - idx = pPage->idxParent; - } - - /* - ** Initialize variables so that it will be safe to jump - ** directly to balance_cleanup at any moment. - */ - nOld = nNew = 0; - sqlite3pager_ref(pParent->aData); - - /* - ** Find sibling pages to pPage and the cells in pParent that divide - ** the siblings. An attempt is made to find NN siblings on either - ** side of pPage. More siblings are taken from one side, however, if - ** pPage there are fewer than NN siblings on the other side. If pParent - ** has NB or fewer children then all children of pParent are taken. - */ - nxDiv = idx - NN; - if( nxDiv + NB > pParent->nCell ){ - nxDiv = pParent->nCell - NB + 1; - } - if( nxDiv<0 ){ - nxDiv = 0; - } - nDiv = 0; - for(i=0, k=nxDiv; i<NB; i++, k++){ - if( k<pParent->nCell ){ - idxDiv[i] = k; - apDiv[i] = findCell(pParent, k); - nDiv++; - assert( !pParent->leaf ); - pgnoOld[i] = get4byte(apDiv[i]); - }else if( k==pParent->nCell ){ - pgnoOld[i] = get4byte(&pParent->aData[pParent->hdrOffset+8]); - }else{ - break; - } - rc = getAndInitPage(pBt, pgnoOld[i], &apOld[i], pParent); - if( rc ) goto balance_cleanup; - apOld[i]->idxParent = k; - apCopy[i] = 0; - assert( i==nOld ); - nOld++; - } - - /* - ** Make copies of the content of pPage and its siblings into aOld[]. - ** The rest of this function will use data from the copies rather - ** that the original pages since the original pages will be in the - ** process of being overwritten. - */ - for(i=0; i<nOld; i++){ - MemPage *p = apCopy[i] = (MemPage*)&aCopy[i][pBt->pageSize]; - p->aData = &((u8*)p)[-pBt->pageSize]; - memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage)); - p->aData = &((u8*)p)[-pBt->pageSize]; - } - - /* - ** Load pointers to all cells on sibling pages and the divider cells - ** into the local apCell[] array. Make copies of the divider cells - ** into space obtained form aSpace[] and remove the the divider Cells - ** from pParent. - ** - ** If the siblings are on leaf pages, then the child pointers of the - ** divider cells are stripped from the cells before they are copied - ** into aSpace[]. In this way, all cells in apCell[] are without - ** child pointers. If siblings are not leaves, then all cell in - ** apCell[] include child pointers. Either way, all cells in apCell[] - ** are alike. - ** - ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. - ** leafData: 1 if pPage holds key+data and pParent holds only keys. - */ - nCell = 0; - leafCorrection = pPage->leaf*4; - leafData = pPage->leafData && pPage->leaf; - for(i=0; i<nOld; i++){ - MemPage *pOld = apCopy[i]; - int limit = pOld->nCell+pOld->nOverflow; - for(j=0; j<limit; j++){ - apCell[nCell] = findOverflowCell(pOld, j); - szCell[nCell] = cellSizePtr(pOld, apCell[nCell]); - nCell++; - } - if( i<nOld-1 ){ - int sz = cellSizePtr(pParent, apDiv[i]); - if( leafData ){ - /* With the LEAFDATA flag, pParent cells hold only INTKEYs that - ** are duplicates of keys on the child pages. We need to remove - ** the divider cells from pParent, but the dividers cells are not - ** added to apCell[] because they are duplicates of child cells. - */ - dropCell(pParent, nxDiv, sz); - }else{ - u8 *pTemp; - szCell[nCell] = sz; - pTemp = &aSpace[iSpace]; - iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); - memcpy(pTemp, apDiv[i], sz); - apCell[nCell] = pTemp+leafCorrection; - dropCell(pParent, nxDiv, sz); - szCell[nCell] -= leafCorrection; - assert( get4byte(pTemp)==pgnoOld[i] ); - if( !pOld->leaf ){ - assert( leafCorrection==0 ); - /* The right pointer of the child page pOld becomes the left - ** pointer of the divider cell */ - memcpy(apCell[nCell], &pOld->aData[pOld->hdrOffset+8], 4); - }else{ - assert( leafCorrection==4 ); - } - nCell++; - } - } - } - - /* - ** Figure out the number of pages needed to hold all nCell cells. - ** Store this number in "k". Also compute szNew[] which is the total - ** size of all cells on the i-th page and cntNew[] which is the index - ** in apCell[] of the cell that divides page i from page i+1. - ** cntNew[k] should equal nCell. - ** - ** Values computed by this block: - ** - ** k: The total number of sibling pages - ** szNew[i]: Spaced used on the i-th sibling page. - ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to - ** the right of the i-th sibling page. - ** usableSpace: Number of bytes of space available on each sibling. - ** - */ - usableSpace = pBt->usableSize - 12 + leafCorrection; - for(subtotal=k=i=0; i<nCell; i++){ - subtotal += szCell[i] + 2; - if( subtotal > usableSpace ){ - szNew[k] = subtotal - szCell[i]; - cntNew[k] = i; - if( leafData ){ i--; } - subtotal = 0; - k++; - } - } - szNew[k] = subtotal; - cntNew[k] = nCell; - k++; - - /* - ** The packing computed by the previous block is biased toward the siblings - ** on the left side. The left siblings are always nearly full, while the - ** right-most sibling might be nearly empty. This block of code attempts - ** to adjust the packing of siblings to get a better balance. - ** - ** This adjustment is more than an optimization. The packing above might - ** be so out of balance as to be illegal. For example, the right-most - ** sibling might be completely empty. This adjustment is not optional. - */ - for(i=k-1; i>0; i--){ - int szRight = szNew[i]; /* Size of sibling on the right */ - int szLeft = szNew[i-1]; /* Size of sibling on the left */ - int r; /* Index of right-most cell in left sibling */ - int d; /* Index of first cell to the left of right sibling */ - - r = cntNew[i-1] - 1; - d = r + 1 - leafData; - while( szRight==0 || szRight+szCell[d]+2<=szLeft-(szCell[r]+2) ){ - szRight += szCell[d] + 2; - szLeft -= szCell[r] + 2; - cntNew[i-1]--; - r = cntNew[i-1] - 1; - d = r + 1 - leafData; - } - szNew[i] = szRight; - szNew[i-1] = szLeft; - } - assert( cntNew[0]>0 ); - - /* - ** Allocate k new pages. Reuse old pages where possible. - */ - assert( pPage->pgno>1 ); - pageFlags = pPage->aData[0]; - for(i=0; i<k; i++){ - MemPage *pNew; - if( i<nOld ){ - pNew = apNew[i] = apOld[i]; - pgnoNew[i] = pgnoOld[i]; - apOld[i] = 0; - sqlite3pager_write(pNew->aData); - }else{ - rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1]); - if( rc ) goto balance_cleanup; - apNew[i] = pNew; - } - nNew++; - zeroPage(pNew, pageFlags); - } - - /* Free any old pages that were not reused as new pages. - */ - while( i<nOld ){ - rc = freePage(apOld[i]); - if( rc ) goto balance_cleanup; - releasePage(apOld[i]); - apOld[i] = 0; - i++; - } - - /* - ** Put the new pages in accending order. This helps to - ** keep entries in the disk file in order so that a scan - ** of the table is a linear scan through the file. That - ** in turn helps the operating system to deliver pages - ** from the disk more rapidly. - ** - ** An O(n^2) insertion sort algorithm is used, but since - ** n is never more than NB (a small constant), that should - ** not be a problem. - ** - ** When NB==3, this one optimization makes the database - ** about 25% faster for large insertions and deletions. - */ - for(i=0; i<k-1; i++){ - int minV = pgnoNew[i]; - int minI = i; - for(j=i+1; j<k; j++){ - if( pgnoNew[j]<(unsigned)minV ){ - minI = j; - minV = pgnoNew[j]; - } - } - if( minI>i ){ - int t; - MemPage *pT; - t = pgnoNew[i]; - pT = apNew[i]; - pgnoNew[i] = pgnoNew[minI]; - apNew[i] = apNew[minI]; - pgnoNew[minI] = t; - apNew[minI] = pT; - } - } - TRACE(("BALANCE: old: %d %d %d new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n", - pgnoOld[0], - nOld>=2 ? pgnoOld[1] : 0, - nOld>=3 ? pgnoOld[2] : 0, - pgnoNew[0], szNew[0], - nNew>=2 ? pgnoNew[1] : 0, nNew>=2 ? szNew[1] : 0, - nNew>=3 ? pgnoNew[2] : 0, nNew>=3 ? szNew[2] : 0, - nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0, - nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0)); - - - /* - ** Evenly distribute the data in apCell[] across the new pages. - ** Insert divider cells into pParent as necessary. - */ - j = 0; - for(i=0; i<nNew; i++){ - MemPage *pNew = apNew[i]; - assert( pNew->pgno==pgnoNew[i] ); - assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]); - j = cntNew[i]; - assert( pNew->nCell>0 ); - assert( pNew->nOverflow==0 ); - if( i<nNew-1 && j<nCell ){ - u8 *pCell; - u8 *pTemp; - int sz; - pCell = apCell[j]; - sz = szCell[j] + leafCorrection; - if( !pNew->leaf ){ - memcpy(&pNew->aData[8], pCell, 4); - pTemp = 0; - }else if( leafData ){ - CellInfo info; - j--; - parseCellPtr(pNew, apCell[j], &info); - pCell = &aSpace[iSpace]; - fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz); - iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); - pTemp = 0; - }else{ - pCell -= 4; - pTemp = &aSpace[iSpace]; - iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); - } - insertCell(pParent, nxDiv, pCell, sz, pTemp); - put4byte(findOverflowCell(pParent,nxDiv), pNew->pgno); - j++; - nxDiv++; - } - } - assert( j==nCell ); - if( (pageFlags & PTF_LEAF)==0 ){ - memcpy(&apNew[nNew-1]->aData[8], &apCopy[nOld-1]->aData[8], 4); - } - if( nxDiv==pParent->nCell+pParent->nOverflow ){ - /* Right-most sibling is the right-most child of pParent */ - put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew[nNew-1]); - }else{ - /* Right-most sibling is the left child of the first entry in pParent - ** past the right-most divider entry */ - put4byte(findOverflowCell(pParent, nxDiv), pgnoNew[nNew-1]); - } - - /* - ** Reparent children of all cells. - */ - for(i=0; i<nNew; i++){ - reparentChildPages(apNew[i]); - } - reparentChildPages(pParent); - - /* - ** Balance the parent page. Note that the current page (pPage) might - ** have been added to the freelist is it might no longer be initialized. - ** But the parent page will always be initialized. - */ - assert( pParent->isInit ); - /* assert( pPage->isInit ); // No! pPage might have been added to freelist */ - /* pageIntegrity(pPage); // No! pPage might have been added to freelist */ - rc = balance(pParent); - - /* - ** Cleanup before returning. - */ -balance_cleanup: - sqliteFree(apCell); - for(i=0; i<nOld; i++){ - releasePage(apOld[i]); - } - for(i=0; i<nNew; i++){ - releasePage(apNew[i]); - } - releasePage(pParent); - TRACE(("BALANCE: finished with %d: old=%d new=%d cells=%d\n", - pPage->pgno, nOld, nNew, nCell)); - return rc; -} - -/* -** This routine is called for the root page of a btree when the root -** page contains no cells. This is an opportunity to make the tree -** shallower by one level. -*/ -static int balance_shallower(MemPage *pPage){ - MemPage *pChild; /* The only child page of pPage */ - Pgno pgnoChild; /* Page number for pChild */ - int rc = SQLITE_OK; /* Return code from subprocedures */ - Btree *pBt; /* The main BTree structure */ - int mxCellPerPage; /* Maximum number of cells per page */ - u8 **apCell; /* All cells from pages being balanced */ - int *szCell; /* Local size of all cells */ - - assert( pPage->pParent==0 ); - assert( pPage->nCell==0 ); - pBt = pPage->pBt; - mxCellPerPage = MX_CELL(pBt); - apCell = sqliteMallocRaw( mxCellPerPage*(sizeof(u8*)+sizeof(int)) ); - if( apCell==0 ) return SQLITE_NOMEM; - szCell = (int*)&apCell[mxCellPerPage]; - if( pPage->leaf ){ - /* The table is completely empty */ - TRACE(("BALANCE: empty table %d\n", pPage->pgno)); - }else{ - /* The root page is empty but has one child. Transfer the - ** information from that one child into the root page if it - ** will fit. This reduces the depth of the tree by one. - ** - ** If the root page is page 1, it has less space available than - ** its child (due to the 100 byte header that occurs at the beginning - ** of the database fle), so it might not be able to hold all of the - ** information currently contained in the child. If this is the - ** case, then do not do the transfer. Leave page 1 empty except - ** for the right-pointer to the child page. The child page becomes - ** the virtual root of the tree. - */ - pgnoChild = get4byte(&pPage->aData[pPage->hdrOffset+8]); - assert( pgnoChild>0 ); - assert( pgnoChild<=sqlite3pager_pagecount(pPage->pBt->pPager) ); - rc = getPage(pPage->pBt, pgnoChild, &pChild); - if( rc ) goto end_shallow_balance; - if( pPage->pgno==1 ){ - rc = initPage(pChild, pPage); - if( rc ) goto end_shallow_balance; - assert( pChild->nOverflow==0 ); - if( pChild->nFree>=100 ){ - /* The child information will fit on the root page, so do the - ** copy */ - int i; - zeroPage(pPage, pChild->aData[0]); - for(i=0; i<pChild->nCell; i++){ - apCell[i] = findCell(pChild,i); - szCell[i] = cellSizePtr(pChild, apCell[i]); - } - assemblePage(pPage, pChild->nCell, apCell, szCell); - freePage(pChild); - TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno)); - }else{ - /* The child has more information that will fit on the root. - ** The tree is already balanced. Do nothing. */ - TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno)); - } - }else{ - memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize); - pPage->isInit = 0; - pPage->pParent = 0; - rc = initPage(pPage, 0); - assert( rc==SQLITE_OK ); - freePage(pChild); - TRACE(("BALANCE: transfer child %d into root %d\n", - pChild->pgno, pPage->pgno)); - } - reparentChildPages(pPage); - releasePage(pChild); - } -end_shallow_balance: - sqliteFree(apCell); - return rc; -} - - -/* -** The root page is overfull -** -** When this happens, Create a new child page and copy the -** contents of the root into the child. Then make the root -** page an empty page with rightChild pointing to the new -** child. Finally, call balance_internal() on the new child -** to cause it to split. -*/ -static int balance_deeper(MemPage *pPage){ - int rc; /* Return value from subprocedures */ - MemPage *pChild; /* Pointer to a new child page */ - Pgno pgnoChild; /* Page number of the new child page */ - Btree *pBt; /* The BTree */ - int usableSize; /* Total usable size of a page */ - u8 *data; /* Content of the parent page */ - u8 *cdata; /* Content of the child page */ - int hdr; /* Offset to page header in parent */ - int brk; /* Offset to content of first cell in parent */ - - assert( pPage->pParent==0 ); - assert( pPage->nOverflow>0 ); - pBt = pPage->pBt; - rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno); - if( rc ) return rc; - assert( sqlite3pager_iswriteable(pChild->aData) ); - usableSize = pBt->usableSize; - data = pPage->aData; - hdr = pPage->hdrOffset; - brk = get2byte(&data[hdr+5]); - cdata = pChild->aData; - memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr); - memcpy(&cdata[brk], &data[brk], usableSize-brk); - rc = initPage(pChild, pPage); - if( rc ) return rc; - memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0])); - pChild->nOverflow = pPage->nOverflow; - if( pChild->nOverflow ){ - pChild->nFree = 0; - } - assert( pChild->nCell==pPage->nCell ); - zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF); - put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild); - TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno)); - rc = balance_nonroot(pChild); - releasePage(pChild); - return rc; -} - -/* -** Decide if the page pPage needs to be balanced. If balancing is -** required, call the appropriate balancing routine. -*/ -static int balance(MemPage *pPage){ - int rc = SQLITE_OK; - if( pPage->pParent==0 ){ - if( pPage->nOverflow>0 ){ - rc = balance_deeper(pPage); - } - if( pPage->nCell==0 ){ - rc = balance_shallower(pPage); - } - }else{ - if( pPage->nOverflow>0 || pPage->nFree>pPage->pBt->usableSize*2/3 ){ - rc = balance_nonroot(pPage); - } - } - return rc; -} - -/* -** This routine checks all cursors that point to table pgnoRoot. -** If any of those cursors other than pExclude were opened with -** wrFlag==0 then this routine returns SQLITE_LOCKED. If all -** cursors that point to pgnoRoot were opened with wrFlag==1 -** then this routine returns SQLITE_OK. -** -** In addition to checking for read-locks (where a read-lock -** means a cursor opened with wrFlag==0) this routine also moves -** all cursors other than pExclude so that they are pointing to the -** first Cell on root page. This is necessary because an insert -** or delete might change the number of cells on a page or delete -** a page entirely and we do not want to leave any cursors -** pointing to non-existant pages or cells. -*/ -static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){ - BtCursor *p; - for(p=pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue; - if( p->wrFlag==0 ) return SQLITE_LOCKED; - if( p->pPage->pgno!=p->pgnoRoot ){ - moveToRoot(p); - } - } - return SQLITE_OK; -} - -/* -** Insert a new record into the BTree. The key is given by (pKey,nKey) -** and the data is given by (pData,nData). The cursor is used only to -** define what table the record should be inserted into. The cursor -** is left pointing at a random location. -** -** For an INTKEY table, only the nKey value of the key is used. pKey is -** ignored. For a ZERODATA table, the pData and nData are both ignored. -*/ -int sqlite3BtreeInsert( - BtCursor *pCur, /* Insert data into the table of this cursor */ - const void *pKey, i64 nKey, /* The key of the new record */ - const void *pData, int nData /* The data of the new record */ -){ - int rc; - int loc; - int szNew; - MemPage *pPage; - Btree *pBt = pCur->pBt; - unsigned char *oldCell; - unsigned char *newCell = 0; - - if( pCur->status ){ - return pCur->status; /* A rollback destroyed this cursor */ - } - if( pBt->inTrans!=TRANS_WRITE ){ - /* Must start a transaction before doing an insert */ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - assert( !pBt->readOnly ); - if( !pCur->wrFlag ){ - return SQLITE_PERM; /* Cursor not open for writing */ - } - if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ - } - rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc); - if( rc ) return rc; - pPage = pCur->pPage; - assert( pPage->intKey || nKey>=0 ); - assert( pPage->leaf || !pPage->leafData ); - TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", - pCur->pgnoRoot, nKey, nData, pPage->pgno, - loc==0 ? "overwrite" : "new entry")); - assert( pPage->isInit ); - rc = sqlite3pager_write(pPage->aData); - if( rc ) return rc; - newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) ); - if( newCell==0 ) return SQLITE_NOMEM; - rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, &szNew); - if( rc ) goto end_insert; - assert( szNew==cellSizePtr(pPage, newCell) ); - assert( szNew<=MX_CELL_SIZE(pBt) ); - if( loc==0 && pCur->isValid ){ - int szOld; - assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); - oldCell = findCell(pPage, pCur->idx); - if( !pPage->leaf ){ - memcpy(newCell, oldCell, 4); - } - szOld = cellSizePtr(pPage, oldCell); - rc = clearCell(pPage, oldCell); - if( rc ) goto end_insert; - dropCell(pPage, pCur->idx, szOld); - }else if( loc<0 && pPage->nCell>0 ){ - assert( pPage->leaf ); - pCur->idx++; - pCur->info.nSize = 0; - }else{ - assert( pPage->leaf ); - } - insertCell(pPage, pCur->idx, newCell, szNew, 0); - rc = balance(pPage); - /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */ - /* fflush(stdout); */ - moveToRoot(pCur); -end_insert: - sqliteFree(newCell); - return rc; -} - -/* -** Delete the entry that the cursor is pointing to. The cursor -** is left pointing at a random location. -*/ -int sqlite3BtreeDelete(BtCursor *pCur){ - MemPage *pPage = pCur->pPage; - unsigned char *pCell; - int rc; - Pgno pgnoChild = 0; - Btree *pBt = pCur->pBt; - - assert( pPage->isInit ); - if( pCur->status ){ - return pCur->status; /* A rollback destroyed this cursor */ - } - if( pBt->inTrans!=TRANS_WRITE ){ - /* Must start a transaction before doing a delete */ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - assert( !pBt->readOnly ); - if( pCur->idx >= pPage->nCell ){ - return SQLITE_ERROR; /* The cursor is not pointing to anything */ - } - if( !pCur->wrFlag ){ - return SQLITE_PERM; /* Did not open this cursor for writing */ - } - if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ - } - rc = sqlite3pager_write(pPage->aData); - if( rc ) return rc; - pCell = findCell(pPage, pCur->idx); - if( !pPage->leaf ){ - pgnoChild = get4byte(pCell); - } - clearCell(pPage, pCell); - if( !pPage->leaf ){ - /* - ** The entry we are about to delete is not a leaf so if we do not - ** do something we will leave a hole on an internal page. - ** We have to fill the hole by moving in a cell from a leaf. The - ** next Cell after the one to be deleted is guaranteed to exist and - ** to be a leaf so we can use it. - */ - BtCursor leafCur; - unsigned char *pNext; - int szNext; - int notUsed; - unsigned char *tempCell; - assert( !pPage->leafData ); - getTempCursor(pCur, &leafCur); - rc = sqlite3BtreeNext(&leafCur, ¬Used); - if( rc!=SQLITE_OK ){ - if( rc!=SQLITE_NOMEM ){ - rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */ - } - return rc; - } - rc = sqlite3pager_write(leafCur.pPage->aData); - if( rc ) return rc; - TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n", - pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno)); - dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell)); - pNext = findCell(leafCur.pPage, leafCur.idx); - szNext = cellSizePtr(leafCur.pPage, pNext); - assert( MX_CELL_SIZE(pBt)>=szNext+4 ); - tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) ); - if( tempCell==0 ) return SQLITE_NOMEM; - insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell); - put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild); - rc = balance(pPage); - sqliteFree(tempCell); - if( rc ) return rc; - dropCell(leafCur.pPage, leafCur.idx, szNext); - rc = balance(leafCur.pPage); - releaseTempCursor(&leafCur); - }else{ - TRACE(("DELETE: table=%d delete from leaf %d\n", - pCur->pgnoRoot, pPage->pgno)); - dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell)); - rc = balance(pPage); - } - moveToRoot(pCur); - return rc; -} - -/* -** Create a new BTree table. Write into *piTable the page -** number for the root page of the new table. -** -** The type of type is determined by the flags parameter. Only the -** following values of flags are currently in use. Other values for -** flags might not work: -** -** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys -** BTREE_ZERODATA Used for SQL indices -*/ -int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ - MemPage *pRoot; - Pgno pgnoRoot; - int rc; - if( pBt->inTrans!=TRANS_WRITE ){ - /* Must start a transaction first */ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - if( pBt->readOnly ){ - return SQLITE_READONLY; - } - rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1); - if( rc ) return rc; - assert( sqlite3pager_iswriteable(pRoot->aData) ); - zeroPage(pRoot, flags | PTF_LEAF); - sqlite3pager_unref(pRoot->aData); - *piTable = (int)pgnoRoot; - return SQLITE_OK; -} - -/* -** Erase the given database page and all its children. Return -** the page to the freelist. -*/ -static int clearDatabasePage( - Btree *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - MemPage *pParent, /* Parent page. NULL for the root */ - int freePageFlag /* Deallocate page if true */ -){ - MemPage *pPage; - int rc; - unsigned char *pCell; - int i; - - rc = getAndInitPage(pBt, pgno, &pPage, pParent); - if( rc ) return rc; - rc = sqlite3pager_write(pPage->aData); - if( rc ) return rc; - for(i=0; i<pPage->nCell; i++){ - pCell = findCell(pPage, i); - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(pCell), pPage->pParent, 1); - if( rc ) return rc; - } - rc = clearCell(pPage, pCell); - if( rc ) return rc; - } - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), pPage->pParent, 1); - if( rc ) return rc; - } - if( freePageFlag ){ - rc = freePage(pPage); - }else{ - zeroPage(pPage, pPage->aData[0] | PTF_LEAF); - } - releasePage(pPage); - return rc; -} - -/* -** Delete all information from a single table in the database. iTable is -** the page number of the root of the table. After this routine returns, -** the root page is empty, but still exists. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** read cursors on the table. Open write cursors are moved to the -** root of the table. -*/ -int sqlite3BtreeClearTable(Btree *pBt, int iTable){ - int rc; - BtCursor *pCur; - if( pBt->inTrans!=TRANS_WRITE ){ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->pgnoRoot==(Pgno)iTable ){ - if( pCur->wrFlag==0 ) return SQLITE_LOCKED; - moveToRoot(pCur); - } - } - rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0); - if( rc ){ - sqlite3BtreeRollback(pBt); - } - return rc; -} - -/* -** Erase all information in a table and add the root of the table to -** the freelist. Except, the root of the principle table (the one on -** page 1) is never added to the freelist. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** cursors on the table. -*/ -int sqlite3BtreeDropTable(Btree *pBt, int iTable){ - int rc; - MemPage *pPage; - BtCursor *pCur; - if( pBt->inTrans!=TRANS_WRITE ){ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->pgnoRoot==(Pgno)iTable ){ - return SQLITE_LOCKED; /* Cannot drop a table that has a cursor */ - } - } - rc = getPage(pBt, (Pgno)iTable, &pPage); - if( rc ) return rc; - rc = sqlite3BtreeClearTable(pBt, iTable); - if( rc ) return rc; - if( iTable>1 ){ - rc = freePage(pPage); - }else{ - zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); - } - releasePage(pPage); - return rc; -} - - -/* -** Read the meta-information out of a database file. Meta[0] -** is the number of free pages currently in the database. Meta[1] -** through meta[15] are available for use by higher layers. Meta[0] -** is read-only, the others are read/write. -** -** The schema layer numbers meta values differently. At the schema -** layer (and the SetCookie and ReadCookie opcodes) the number of -** free pages is not visible. So Cookie[0] is the same as Meta[1]. -*/ -int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){ - int rc; - unsigned char *pP1; - - assert( idx>=0 && idx<=15 ); - rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1); - if( rc ) return rc; - *pMeta = get4byte(&pP1[36 + idx*4]); - sqlite3pager_unref(pP1); - - /* The current implementation is unable to handle writes to an autovacuumed - ** database. So make such a database readonly. */ - if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; - - return SQLITE_OK; -} - -/* -** Write meta-information back into the database. Meta[0] is -** read-only and may not be written. -*/ -int sqlite3BtreeUpdateMeta(Btree *pBt, int idx, u32 iMeta){ - unsigned char *pP1; - int rc; - assert( idx>=1 && idx<=15 ); - if( pBt->inTrans!=TRANS_WRITE ){ - return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; - } - assert( pBt->pPage1!=0 ); - pP1 = pBt->pPage1->aData; - rc = sqlite3pager_write(pP1); - if( rc ) return rc; - put4byte(&pP1[36 + idx*4], iMeta); - return SQLITE_OK; -} - -/* -** Return the flag byte at the beginning of the page that the cursor -** is currently pointing to. -*/ -int sqlite3BtreeFlags(BtCursor *pCur){ - MemPage *pPage = pCur->pPage; - return pPage ? pPage->aData[pPage->hdrOffset] : 0; -} - -/* -** Print a disassembly of the given page on standard output. This routine -** is used for debugging and testing only. -*/ -#ifdef SQLITE_TEST -int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){ - int rc; - MemPage *pPage; - int i, j, c; - int nFree; - u16 idx; - int hdr; - int nCell; - int isInit; - unsigned char *data; - char range[20]; - unsigned char payload[20]; - - rc = getPage(pBt, (Pgno)pgno, &pPage); - isInit = pPage->isInit; - if( pPage->isInit==0 ){ - initPage(pPage, 0); - } - if( rc ){ - return rc; - } - hdr = pPage->hdrOffset; - data = pPage->aData; - c = data[hdr]; - pPage->intKey = (c & (PTF_INTKEY|PTF_LEAFDATA))!=0; - pPage->zeroData = (c & PTF_ZERODATA)!=0; - pPage->leafData = (c & PTF_LEAFDATA)!=0; - pPage->leaf = (c & PTF_LEAF)!=0; - pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData)); - nCell = get2byte(&data[hdr+3]); - sqlite3DebugPrintf("PAGE %d: flags=0x%02x frag=%d parent=%d\n", pgno, - data[hdr], data[hdr+7], - (pPage->isInit && pPage->pParent) ? pPage->pParent->pgno : 0); - assert( hdr == (pgno==1 ? 100 : 0) ); - idx = hdr + 12 - pPage->leaf*4; - for(i=0; i<nCell; i++){ - CellInfo info; - Pgno child; - unsigned char *pCell; - int sz; - int addr; - - addr = get2byte(&data[idx + 2*i]); - pCell = &data[addr]; - parseCellPtr(pPage, pCell, &info); - sz = info.nSize; - sprintf(range,"%d..%d", addr, addr+sz-1); - if( pPage->leaf ){ - child = 0; - }else{ - child = get4byte(pCell); - } - sz = info.nData; - if( !pPage->intKey ) sz += info.nKey; - if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1; - memcpy(payload, &pCell[info.nHeader], sz); - for(j=0; j<sz; j++){ - if( payload[j]<0x20 || payload[j]>0x7f ) payload[j] = '.'; - } - payload[sz] = 0; - sqlite3DebugPrintf( - "cell %2d: i=%-10s chld=%-4d nk=%-4lld nd=%-4d payload=%s\n", - i, range, child, info.nKey, info.nData, payload - ); - } - if( !pPage->leaf ){ - sqlite3DebugPrintf("right_child: %d\n", get4byte(&data[hdr+8])); - } - nFree = 0; - i = 0; - idx = get2byte(&data[hdr+1]); - while( idx>0 && idx<pPage->pBt->usableSize ){ - int sz = get2byte(&data[idx+2]); - sprintf(range,"%d..%d", idx, idx+sz-1); - nFree += sz; - sqlite3DebugPrintf("freeblock %2d: i=%-10s size=%-4d total=%d\n", - i, range, sz, nFree); - idx = get2byte(&data[idx]); - i++; - } - if( idx!=0 ){ - sqlite3DebugPrintf("ERROR: next freeblock index out of range: %d\n", idx); - } - if( recursive && !pPage->leaf ){ - for(i=0; i<nCell; i++){ - unsigned char *pCell = findCell(pPage, i); - sqlite3BtreePageDump(pBt, get4byte(pCell), 1); - idx = get2byte(pCell); - } - sqlite3BtreePageDump(pBt, get4byte(&data[hdr+8]), 1); - } - pPage->isInit = isInit; - sqlite3pager_unref(data); - fflush(stdout); - return SQLITE_OK; -} -#endif - -#ifdef SQLITE_TEST -/* -** Fill aResult[] with information about the entry and page that the -** cursor is pointing to. -** -** aResult[0] = The page number -** aResult[1] = The entry number -** aResult[2] = Total number of entries on this page -** aResult[3] = Cell size (local payload + header) -** aResult[4] = Number of free bytes on this page -** aResult[5] = Number of free blocks on the page -** aResult[6] = Total payload size (local + overflow) -** aResult[7] = Header size in bytes -** aResult[8] = Local payload size -** aResult[9] = Parent page number -** -** This routine is used for testing and debugging only. -*/ -int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){ - int cnt, idx; - MemPage *pPage = pCur->pPage; - BtCursor tmpCur; - - pageIntegrity(pPage); - assert( pPage->isInit ); - getTempCursor(pCur, &tmpCur); - while( upCnt-- ){ - moveToParent(&tmpCur); - } - pPage = tmpCur.pPage; - pageIntegrity(pPage); - aResult[0] = sqlite3pager_pagenumber(pPage->aData); - assert( aResult[0]==pPage->pgno ); - aResult[1] = tmpCur.idx; - aResult[2] = pPage->nCell; - if( tmpCur.idx>=0 && tmpCur.idx<pPage->nCell ){ - getCellInfo(&tmpCur); - aResult[3] = tmpCur.info.nSize; - aResult[6] = tmpCur.info.nData; - aResult[7] = tmpCur.info.nHeader; - aResult[8] = tmpCur.info.nLocal; - }else{ - aResult[3] = 0; - aResult[6] = 0; - aResult[7] = 0; - aResult[8] = 0; - } - aResult[4] = pPage->nFree; - cnt = 0; - idx = get2byte(&pPage->aData[pPage->hdrOffset+1]); - while( idx>0 && idx<pPage->pBt->usableSize ){ - cnt++; - idx = get2byte(&pPage->aData[idx]); - } - aResult[5] = cnt; - if( pPage->pParent==0 || isRootPage(pPage) ){ - aResult[9] = 0; - }else{ - aResult[9] = pPage->pParent->pgno; - } - releaseTempCursor(&tmpCur); - return SQLITE_OK; -} -#endif - -/* -** Return the pager associated with a BTree. This routine is used for -** testing and debugging only. -*/ -Pager *sqlite3BtreePager(Btree *pBt){ - return pBt->pPager; -} - -/* -** This structure is passed around through all the sanity checking routines -** in order to keep track of some global state information. -*/ -typedef struct IntegrityCk IntegrityCk; -struct IntegrityCk { - Btree *pBt; /* The tree being checked out */ - Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ - int nPage; /* Number of pages in the database */ - int *anRef; /* Number of times each page is referenced */ - char *zErrMsg; /* An error message. NULL of no errors seen. */ -}; - -/* -** Append a message to the error message string. -*/ -static void checkAppendMsg( - IntegrityCk *pCheck, - char *zMsg1, - const char *zFormat, - ... -){ - va_list ap; - char *zMsg2; - va_start(ap, zFormat); - zMsg2 = sqlite3VMPrintf(zFormat, ap); - va_end(ap); - if( zMsg1==0 ) zMsg1 = ""; - if( pCheck->zErrMsg ){ - char *zOld = pCheck->zErrMsg; - pCheck->zErrMsg = 0; - sqlite3SetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0); - sqliteFree(zOld); - }else{ - sqlite3SetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0); - } - sqliteFree(zMsg2); -} - -/* -** Add 1 to the reference count for page iPage. If this is the second -** reference to the page, add an error message to pCheck->zErrMsg. -** Return 1 if there are 2 ore more references to the page and 0 if -** if this is the first reference to the page. -** -** Also check that the page number is in bounds. -*/ -static int checkRef(IntegrityCk *pCheck, int iPage, char *zContext){ - if( iPage==0 ) return 1; - if( iPage>pCheck->nPage || iPage<0 ){ - checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); - return 1; - } - if( pCheck->anRef[iPage]==1 ){ - checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); - return 1; - } - return (pCheck->anRef[iPage]++)>1; -} - -/* -** Check the integrity of the freelist or of an overflow page list. -** Verify that the number of pages on the list is N. -*/ -static void checkList( - IntegrityCk *pCheck, /* Integrity checking context */ - int isFreeList, /* True for a freelist. False for overflow page list */ - int iPage, /* Page number for first page in the list */ - int N, /* Expected number of pages in the list */ - char *zContext /* Context for error messages */ -){ - int i; - int expected = N; - int iFirst = iPage; - while( N-- > 0 ){ - unsigned char *pOvfl; - if( iPage<1 ){ - checkAppendMsg(pCheck, zContext, - "%d of %d pages missing from overflow list starting at %d", - N+1, expected, iFirst); - break; - } - if( checkRef(pCheck, iPage, zContext) ) break; - if( sqlite3pager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){ - checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); - break; - } - if( isFreeList ){ - int n = get4byte(&pOvfl[4]); - if( n>pCheck->pBt->usableSize/4-8 ){ - checkAppendMsg(pCheck, zContext, - "freelist leaf count too big on page %d", iPage); - N--; - }else{ - for(i=0; i<n; i++){ - checkRef(pCheck, get4byte(&pOvfl[8+i*4]), zContext); - } - N -= n; - } - } - iPage = get4byte(pOvfl); - sqlite3pager_unref(pOvfl); - } -} - -/* -** Do various sanity checks on a single page of a tree. Return -** the tree depth. Root pages return 0. Parents of root pages -** return 1, and so forth. -** -** These checks are done: -** -** 1. Make sure that cells and freeblocks do not overlap -** but combine to completely cover the page. -** NO 2. Make sure cell keys are in order. -** NO 3. Make sure no key is less than or equal to zLowerBound. -** NO 4. Make sure no key is greater than or equal to zUpperBound. -** 5. Check the integrity of overflow pages. -** 6. Recursively call checkTreePage on all children. -** 7. Verify that the depth of all children is the same. -** 8. Make sure this page is at least 33% full or else it is -** the root of the tree. -*/ -static int checkTreePage( - IntegrityCk *pCheck, /* Context for the sanity check */ - int iPage, /* Page number of the page to check */ - MemPage *pParent, /* Parent page */ - char *zParentContext, /* Parent context */ - char *zLowerBound, /* All keys should be greater than this, if not NULL */ - int nLower, /* Number of characters in zLowerBound */ - char *zUpperBound, /* All keys should be less than this, if not NULL */ - int nUpper /* Number of characters in zUpperBound */ -){ - MemPage *pPage; - int i, rc, depth, d2, pgno, cnt; - int hdr, cellStart; - int nCell; - u8 *data; - BtCursor cur; - Btree *pBt; - int maxLocal, usableSize; - char zContext[100]; - char *hit; - - /* Check that the page exists - */ - cur.pBt = pBt = pCheck->pBt; - usableSize = pBt->usableSize; - if( iPage==0 ) return 0; - if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = getPage(pBt, (Pgno)iPage, &pPage))!=0 ){ - checkAppendMsg(pCheck, zContext, - "unable to get the page. error code=%d", rc); - return 0; - } - maxLocal = pPage->leafData ? pBt->maxLeaf : pBt->maxLocal; - if( (rc = initPage(pPage, pParent))!=0 ){ - checkAppendMsg(pCheck, zContext, "initPage() returns error code %d", rc); - releasePage(pPage); - return 0; - } - - /* Check out all the cells. - */ - depth = 0; - cur.pPage = pPage; - for(i=0; i<pPage->nCell; i++){ - u8 *pCell; - int sz; - CellInfo info; - - /* Check payload overflow pages - */ - sprintf(zContext, "On tree page %d cell %d: ", iPage, i); - pCell = findCell(pPage,i); - parseCellPtr(pPage, pCell, &info); - sz = info.nData; - if( !pPage->intKey ) sz += info.nKey; - if( sz>info.nLocal ){ - int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); - checkList(pCheck, 0, get4byte(&pCell[info.iOverflow]),nPage,zContext); - } - - /* Check sanity of left child page. - */ - if( !pPage->leaf ){ - pgno = get4byte(pCell); - d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0); - if( i>0 && d2!=depth ){ - checkAppendMsg(pCheck, zContext, "Child page depth differs"); - } - depth = d2; - } - } - if( !pPage->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - sprintf(zContext, "On page %d at right child: ", iPage); - checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0); - } - - /* Check for complete coverage of the page - */ - data = pPage->aData; - hdr = pPage->hdrOffset; - hit = sqliteMalloc( usableSize ); - if( hit ){ - memset(hit, 1, get2byte(&data[hdr+5])); - nCell = get2byte(&data[hdr+3]); - cellStart = hdr + 12 - 4*pPage->leaf; - for(i=0; i<nCell; i++){ - int pc = get2byte(&data[cellStart+i*2]); - int size = cellSizePtr(pPage, &data[pc]); - int j; - for(j=pc+size-1; j>=pc; j--) hit[j]++; - } - for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i<usableSize && cnt<10000; - cnt++){ - int size = get2byte(&data[i+2]); - int j; - for(j=i+size-1; j>=i; j--) hit[j]++; - i = get2byte(&data[i]); - } - for(i=cnt=0; i<usableSize; i++){ - if( hit[i]==0 ){ - cnt++; - }else if( hit[i]>1 ){ - checkAppendMsg(pCheck, 0, - "Multiple uses for byte %d of page %d", i, iPage); - break; - } - } - if( cnt!=data[hdr+7] ){ - checkAppendMsg(pCheck, 0, - "Fragmented space is %d byte reported as %d on page %d", - cnt, data[hdr+7], iPage); - } - } - sqliteFree(hit); - - releasePage(pPage); - return depth+1; -} - -/* -** This routine does a complete check of the given BTree file. aRoot[] is -** an array of pages numbers were each page number is the root page of -** a table. nRoot is the number of entries in aRoot. -** -** If everything checks out, this routine returns NULL. If something is -** amiss, an error message is written into memory obtained from malloc() -** and a pointer to that error message is returned. The calling function -** is responsible for freeing the error message when it is done. -*/ -char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ - int i; - int nRef; - IntegrityCk sCheck; - - nRef = *sqlite3pager_stats(pBt->pPager); - if( lockBtree(pBt)!=SQLITE_OK ){ - return sqliteStrDup("Unable to acquire a read lock on the database"); - } - sCheck.pBt = pBt; - sCheck.pPager = pBt->pPager; - sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager); - if( sCheck.nPage==0 ){ - unlockBtreeIfUnused(pBt); - return 0; - } - sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); - for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } - i = PENDING_BYTE/pBt->pageSize + 1; - if( i<=sCheck.nPage ){ - sCheck.anRef[i] = 1; - } - sCheck.zErrMsg = 0; - - /* Check the integrity of the freelist - */ - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), - get4byte(&pBt->pPage1->aData[36]), "Main freelist: "); - - /* Check all the tables. - */ - for(i=0; i<nRoot; i++){ - if( aRoot[i]==0 ) continue; - checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0); - } - - /* Make sure every page in the file is referenced - */ - for(i=1; i<=sCheck.nPage; i++){ - if( sCheck.anRef[i]==0 ){ - checkAppendMsg(&sCheck, 0, "Page %d is never used", i); - } - } - - /* Make sure this analysis did not leave any unref() pages - */ - unlockBtreeIfUnused(pBt); - if( nRef != *sqlite3pager_stats(pBt->pPager) ){ - checkAppendMsg(&sCheck, 0, - "Outstanding page count goes from %d to %d during this analysis", - nRef, *sqlite3pager_stats(pBt->pPager) - ); - } - - /* Clean up and report errors. - */ - sqliteFree(sCheck.anRef); - return sCheck.zErrMsg; -} - -/* -** Return the full pathname of the underlying database file. -*/ -const char *sqlite3BtreeGetFilename(Btree *pBt){ - assert( pBt->pPager!=0 ); - return sqlite3pager_filename(pBt->pPager); -} - -/* -** Return the pathname of the directory that contains the database file. -*/ -const char *sqlite3BtreeGetDirname(Btree *pBt){ - assert( pBt->pPager!=0 ); - return sqlite3pager_dirname(pBt->pPager); -} - -/* -** Return the pathname of the journal file for this database. The return -** value of this routine is the same regardless of whether the journal file -** has been created or not. -*/ -const char *sqlite3BtreeGetJournalname(Btree *pBt){ - assert( pBt->pPager!=0 ); - return sqlite3pager_journalname(pBt->pPager); -} - -/* -** Copy the complete content of pBtFrom into pBtTo. A transaction -** must be active for both files. -** -** The size of file pBtFrom may be reduced by this operation. -** If anything goes wrong, the transaction on pBtFrom is rolled back. -*/ -int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ - int rc = SQLITE_OK; - Pgno i, nPage, nToPage; - - if( pBtTo->inTrans!=TRANS_WRITE || pBtFrom->inTrans!=TRANS_WRITE ){ - return SQLITE_ERROR; - } - if( pBtTo->pCursor ) return SQLITE_BUSY; - nToPage = sqlite3pager_pagecount(pBtTo->pPager); - nPage = sqlite3pager_pagecount(pBtFrom->pPager); - for(i=1; rc==SQLITE_OK && i<=nPage; i++){ - void *pPage; - rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage); - if( rc ) break; - rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage); - if( rc ) break; - sqlite3pager_unref(pPage); - } - for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){ - void *pPage; - rc = sqlite3pager_get(pBtTo->pPager, i, &pPage); - if( rc ) break; - rc = sqlite3pager_write(pPage); - sqlite3pager_unref(pPage); - sqlite3pager_dont_write(pBtTo->pPager, i); - } - if( !rc && nPage<nToPage ){ - rc = sqlite3pager_truncate(pBtTo->pPager, nPage); - } - if( rc ){ - sqlite3BtreeRollback(pBtTo); - } - return rc; -} - -/* -** Return non-zero if a transaction is active. -*/ -int sqlite3BtreeIsInTrans(Btree *pBt){ - return (pBt && (pBt->inTrans==TRANS_WRITE)); -} - -/* -** Return non-zero if a statement transaction is active. -*/ -int sqlite3BtreeIsInStmt(Btree *pBt){ - return (pBt && pBt->inStmt); -} - -/* -** This call is a no-op if no write-transaction is currently active on pBt. -** -** Otherwise, sync the database file for the btree pBt. zMaster points to -** the name of a master journal file that should be written into the -** individual journal file, or is NULL, indicating no master journal file -** (single database transaction). -** -** When this is called, the master journal should already have been -** created, populated with this journal pointer and synced to disk. -** -** Once this is routine has returned, the only thing required to commit -** the write-transaction for this database file is to delete the journal. -*/ -int sqlite3BtreeSync(Btree *pBt, const char *zMaster){ - if( pBt->inTrans==TRANS_WRITE ){ - return sqlite3pager_sync(pBt->pPager, zMaster); - } - return SQLITE_OK; -} diff --git a/kopete/plugins/statistics/sqlite/btree.h b/kopete/plugins/statistics/sqlite/btree.h deleted file mode 100644 index 48524aef..00000000 --- a/kopete/plugins/statistics/sqlite/btree.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the sqlite B-Tree file -** subsystem. See comments in the source code for a detailed description -** of what each interface routine does. -** -** @(#) $Id$ -*/ -#ifndef _BTREE_H_ -#define _BTREE_H_ - -/* TODO: This definition is just included so other modules compile. It -** needs to be revisited. -*/ -#define SQLITE_N_BTREE_META 10 - -/* -** Forward declarations of structure -*/ -typedef struct Btree Btree; -typedef struct BtCursor BtCursor; - - -int sqlite3BtreeOpen( - const char *zFilename, /* Name of database file to open */ - Btree **, /* Return open Btree* here */ - int flags /* Flags */ -); - -/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the -** following values. -*/ -#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ -#define BTREE_MEMORY 2 /* In-memory DB. No argument */ - -int sqlite3BtreeClose(Btree*); -int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*); -int sqlite3BtreeSetCacheSize(Btree*,int); -int sqlite3BtreeSetSafetyLevel(Btree*,int); -int sqlite3BtreeSetPageSize(Btree*,int,int); -int sqlite3BtreeGetPageSize(Btree*); -int sqlite3BtreeGetReserve(Btree*); -int sqlite3BtreeBeginTrans(Btree*,int); -int sqlite3BtreeCommit(Btree*); -int sqlite3BtreeRollback(Btree*); -int sqlite3BtreeBeginStmt(Btree*); -int sqlite3BtreeCommitStmt(Btree*); -int sqlite3BtreeRollbackStmt(Btree*); -int sqlite3BtreeCreateTable(Btree*, int*, int flags); -int sqlite3BtreeIsInTrans(Btree*); -int sqlite3BtreeIsInStmt(Btree*); -int sqlite3BtreeSync(Btree*, const char *zMaster); - -const char *sqlite3BtreeGetFilename(Btree *); -const char *sqlite3BtreeGetDirname(Btree *); -const char *sqlite3BtreeGetJournalname(Btree *); -int sqlite3BtreeCopyFile(Btree *, Btree *); - -/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR -** of the following flags: -*/ -#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ -#define BTREE_ZERODATA 2 /* Table has keys only - no data */ -#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ - -int sqlite3BtreeDropTable(Btree*, int); -int sqlite3BtreeClearTable(Btree*, int); -int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); -int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); - -int sqlite3BtreeCursor( - Btree*, /* BTree containing table to open */ - int iTable, /* Index of root page */ - int wrFlag, /* 1 for writing. 0 for read-only */ - int(*)(void*,int,const void*,int,const void*), /* Key comparison function */ - void*, /* First argument to compare function */ - BtCursor **ppCursor /* Returned cursor */ -); - -void sqlite3BtreeSetCompare( - BtCursor *, - int(*)(void*,int,const void*,int,const void*), - void* -); - -int sqlite3BtreeCloseCursor(BtCursor*); -int sqlite3BtreeMoveto(BtCursor*, const void *pKey, i64 nKey, int *pRes); -int sqlite3BtreeDelete(BtCursor*); -int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, - const void *pData, int nData); -int sqlite3BtreeFirst(BtCursor*, int *pRes); -int sqlite3BtreeLast(BtCursor*, int *pRes); -int sqlite3BtreeNext(BtCursor*, int *pRes); -int sqlite3BtreeEof(BtCursor*); -int sqlite3BtreeFlags(BtCursor*); -int sqlite3BtreePrevious(BtCursor*, int *pRes); -int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); -int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); -const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); -int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); -int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); - -char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot); -struct Pager *sqlite3BtreePager(Btree*); - - -#ifdef SQLITE_TEST -int sqlite3BtreeCursorInfo(BtCursor*, int*, int); -void sqlite3BtreeCursorList(Btree*); -int sqlite3BtreePageDump(Btree*, int, int recursive); -#endif - - -#endif /* _BTREE_H_ */ diff --git a/kopete/plugins/statistics/sqlite/build.c b/kopete/plugins/statistics/sqlite/build.c deleted file mode 100644 index 3e5e08a5..00000000 --- a/kopete/plugins/statistics/sqlite/build.c +++ /dev/null @@ -1,2564 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the SQLite parser -** when syntax rules are reduced. The routines in this file handle the -** following kinds of SQL syntax: -** -** CREATE TABLE -** DROP TABLE -** CREATE INDEX -** DROP INDEX -** creating ID lists -** BEGIN TRANSACTION -** COMMIT -** ROLLBACK -** PRAGMA -** -** $Id$ -*/ -#include "sqliteInt.h" -#include <ctype.h> - -/* -** This routine is called when a new SQL statement is beginning to -** be parsed. Check to see if the schema for the database needs -** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables. -** If it does, then read it. -*/ -void sqlite3BeginParse(Parse *pParse, int explainFlag){ - pParse->explain = explainFlag; - pParse->nVar = 0; -} - -/* -** This routine is called after a single SQL statement has been -** parsed and a VDBE program to execute that statement has been -** prepared. This routine puts the finishing touches on the -** VDBE program and resets the pParse structure for the next -** parse. -** -** Note that if an error occurred, it might be the case that -** no VDBE code was generated. -*/ -void sqlite3FinishCoding(Parse *pParse){ - sqlite3 *db; - Vdbe *v; - - if( sqlite3_malloc_failed ) return; - - /* Begin by generating some termination code at the end of the - ** vdbe program - */ - db = pParse->db; - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp(v, OP_Halt, 0, 0); - - /* The cookie mask contains one bit for each database file open. - ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are - ** set for each database that is used. Generate code to start a - ** transaction on each used database and to verify the schema cookie - ** on each used database. - */ - if( pParse->cookieGoto>0 ){ - u32 mask; - int iDb; - sqlite3VdbeChangeP2(v, pParse->cookieGoto-1, sqlite3VdbeCurrentAddr(v)); - for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ - if( (mask & pParse->cookieMask)==0 ) continue; - sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0); - sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); - } - sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto); - } - - /* Add a No-op that contains the complete text of the compiled SQL - ** statement as its P3 argument. This does not change the functionality - ** of the program. - ** - ** This is used to implement sqlite3_trace() functionality. - */ - sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql); - } - - - /* Get the VDBE program ready for execution - */ - if( v && pParse->nErr==0 ){ - FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; - sqlite3VdbeTrace(v, trace); - sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3, - pParse->nTab+3, pParse->explain); - pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE; - pParse->colNamesSet = 0; - }else if( pParse->rc==SQLITE_OK ){ - pParse->rc = SQLITE_ERROR; - } - pParse->nTab = 0; - pParse->nMem = 0; - pParse->nSet = 0; - pParse->nAgg = 0; - pParse->nVar = 0; - pParse->cookieMask = 0; - pParse->cookieGoto = 0; -} - -/* -** Locate the in-memory structure that describes a particular database -** table given the name of that table and (optionally) the name of the -** database containing the table. Return NULL if not found. -** -** If zDatabase is 0, all databases are searched for the table and the -** first matching table is returned. (No checking for duplicate table -** names is done.) The search order is TEMP first, then MAIN, then any -** auxiliary databases added using the ATTACH command. -** -** See also sqlite3LocateTable(). -*/ -Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ - Table *p = 0; - int i; - assert( zName!=0 ); - assert( (db->flags & SQLITE_Initialized) || db->init.busy ); - for(i=0; i<db->nDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; - p = sqlite3HashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1); - if( p ) break; - } - return p; -} - -/* -** Locate the in-memory structure that describes a particular database -** table given the name of that table and (optionally) the name of the -** database containing the table. Return NULL if not found. Also leave an -** error message in pParse->zErrMsg. -** -** The difference between this routine and sqlite3FindTable() is that this -** routine leaves an error message in pParse->zErrMsg where -** sqlite3FindTable() does not. -*/ -Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){ - Table *p; - - /* Read the database schema. If an error occurs, leave an error message - ** and code in pParse and return NULL. */ - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - return 0; - } - - p = sqlite3FindTable(pParse->db, zName, zDbase); - if( p==0 ){ - if( zDbase ){ - sqlite3ErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); - }else if( sqlite3FindTable(pParse->db, zName, 0)!=0 ){ - sqlite3ErrorMsg(pParse, "table \"%s\" is not in database \"%s\"", - zName, zDbase); - }else{ - sqlite3ErrorMsg(pParse, "no such table: %s", zName); - } - pParse->checkSchema = 1; - } - return p; -} - -/* -** Locate the in-memory structure that describes -** a particular index given the name of that index -** and the name of the database that contains the index. -** Return NULL if not found. -** -** If zDatabase is 0, all databases are searched for the -** table and the first matching index is returned. (No checking -** for duplicate index names is done.) The search order is -** TEMP first, then MAIN, then any auxiliary databases added -** using the ATTACH command. -*/ -Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ - Index *p = 0; - int i; - assert( (db->flags & SQLITE_Initialized) || db->init.busy ); - for(i=0; i<db->nDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; - p = sqlite3HashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1); - if( p ) break; - } - return p; -} - -/* -** Reclaim the memory used by an index -*/ -static void freeIndex(Index *p){ - sqliteFree(p->zColAff); - sqliteFree(p); -} - -/* -** Remove the given index from the index hash table, and free -** its memory structures. -** -** The index is removed from the database hash tables but -** it is not unlinked from the Table that it indexes. -** Unlinking from the Table must be done by the calling function. -*/ -static void sqliteDeleteIndex(sqlite3 *db, Index *p){ - Index *pOld; - - assert( db!=0 && p->zName!=0 ); - pOld = sqlite3HashInsert(&db->aDb[p->iDb].idxHash, p->zName, - strlen(p->zName)+1, 0); - if( pOld!=0 && pOld!=p ){ - sqlite3HashInsert(&db->aDb[p->iDb].idxHash, pOld->zName, - strlen(pOld->zName)+1, pOld); - } - freeIndex(p); -} - -/* -** Unlink the given index from its table, then remove -** the index from the index hash table and free its memory -** structures. -*/ -void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ - Index *pIndex; - int len; - - len = strlen(zIdxName); - pIndex = sqlite3HashInsert(&db->aDb[iDb].idxHash, zIdxName, len+1, 0); - if( pIndex ){ - if( pIndex->pTable->pIndex==pIndex ){ - pIndex->pTable->pIndex = pIndex->pNext; - }else{ - Index *p; - for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} - if( p && p->pNext==pIndex ){ - p->pNext = pIndex->pNext; - } - } - freeIndex(pIndex); - } - db->flags |= SQLITE_InternChanges; -} - -/* -** Erase all schema information from the in-memory hash tables of -** a single database. This routine is called to reclaim memory -** before the database closes. It is also called during a rollback -** if there were schema changes during the transaction or if a -** schema-cookie mismatch occurs. -** -** If iDb<=0 then reset the internal schema tables for all database -** files. If iDb>=2 then reset the internal schema for only the -** single file indicated. -*/ -void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ - HashElem *pElem; - Hash temp1; - Hash temp2; - int i, j; - - assert( iDb>=0 && iDb<db->nDb ); - db->flags &= ~SQLITE_Initialized; - for(i=iDb; i<db->nDb; i++){ - Db *pDb = &db->aDb[i]; - temp1 = pDb->tblHash; - temp2 = pDb->trigHash; - sqlite3HashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0); - sqlite3HashClear(&pDb->aFKey); - sqlite3HashClear(&pDb->idxHash); - for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ - Trigger *pTrigger = sqliteHashData(pElem); - sqlite3DeleteTrigger(pTrigger); - } - sqlite3HashClear(&temp2); - sqlite3HashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0); - for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTab = sqliteHashData(pElem); - sqlite3DeleteTable(db, pTab); - } - sqlite3HashClear(&temp1); - DbClearProperty(db, i, DB_SchemaLoaded); - if( iDb>0 ) return; - } - assert( iDb==0 ); - db->flags &= ~SQLITE_InternChanges; - - /* If one or more of the auxiliary database files has been closed, - ** then remove then from the auxiliary database list. We take the - ** opportunity to do this here since we have just deleted all of the - ** schema hash tables and therefore do not have to make any changes - ** to any of those tables. - */ - for(i=0; i<db->nDb; i++){ - struct Db *pDb = &db->aDb[i]; - if( pDb->pBt==0 ){ - if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux); - pDb->pAux = 0; - } - } - for(i=j=2; i<db->nDb; i++){ - struct Db *pDb = &db->aDb[i]; - if( pDb->pBt==0 ){ - sqliteFree(pDb->zName); - pDb->zName = 0; - continue; - } - if( j<i ){ - db->aDb[j] = db->aDb[i]; - } - j++; - } - memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); - db->nDb = j; - if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ - memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); - sqliteFree(db->aDb); - db->aDb = db->aDbStatic; - } -} - -/* -** This routine is called whenever a rollback occurs. If there were -** schema changes during the transaction, then we have to reset the -** internal hash tables and reload them from disk. -*/ -void sqlite3RollbackInternalChanges(sqlite3 *db){ - if( db->flags & SQLITE_InternChanges ){ - sqlite3ResetInternalSchema(db, 0); - } -} - -/* -** This routine is called when a commit occurs. -*/ -void sqlite3CommitInternalChanges(sqlite3 *db){ - db->flags &= ~SQLITE_InternChanges; -} - -/* -** Clear the column names from a table or view. -*/ -static void sqliteResetColumnNames(Table *pTable){ - int i; - Column *pCol; - assert( pTable!=0 ); - for(i=0, pCol=pTable->aCol; i<pTable->nCol; i++, pCol++){ - sqliteFree(pCol->zName); - sqliteFree(pCol->zDflt); - sqliteFree(pCol->zType); - } - sqliteFree(pTable->aCol); - pTable->aCol = 0; - pTable->nCol = 0; -} - -/* -** Remove the memory data structures associated with the given -** Table. No changes are made to disk by this routine. -** -** This routine just deletes the data structure. It does not unlink -** the table data structure from the hash table. Nor does it remove -** foreign keys from the sqlite.aFKey hash table. But it does destroy -** memory structures of the indices and foreign keys associated with -** the table. -** -** Indices associated with the table are unlinked from the "db" -** data structure if db!=NULL. If db==NULL, indices attached to -** the table are deleted, but it is assumed they have already been -** unlinked. -*/ -void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ - Index *pIndex, *pNext; - FKey *pFKey, *pNextFKey; - - if( pTable==0 ) return; - - /* Delete all indices associated with this table - */ - for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ - pNext = pIndex->pNext; - assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) ); - sqliteDeleteIndex(db, pIndex); - } - - /* Delete all foreign keys associated with this table. The keys - ** should have already been unlinked from the db->aFKey hash table - */ - for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ - pNextFKey = pFKey->pNextFrom; - assert( pTable->iDb<db->nDb ); - assert( sqlite3HashFind(&db->aDb[pTable->iDb].aFKey, - pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); - sqliteFree(pFKey); - } - - /* Delete the Table structure itself. - */ - sqliteResetColumnNames(pTable); - sqliteFree(pTable->zName); - sqliteFree(pTable->zColAff); - sqlite3SelectDelete(pTable->pSelect); - sqliteFree(pTable); -} - -/* -** Unlink the given table from the hash tables and the delete the -** table structure with all its indices and foreign keys. -*/ -void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ - Table *p; - FKey *pF1, *pF2; - Db *pDb; - - assert( db!=0 ); - assert( iDb>=0 && iDb<db->nDb ); - assert( zTabName && zTabName[0] ); - pDb = &db->aDb[iDb]; - p = sqlite3HashInsert(&pDb->tblHash, zTabName, strlen(zTabName)+1, 0); - if( p ){ - for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){ - int nTo = strlen(pF1->zTo) + 1; - pF2 = sqlite3HashFind(&pDb->aFKey, pF1->zTo, nTo); - if( pF2==pF1 ){ - sqlite3HashInsert(&pDb->aFKey, pF1->zTo, nTo, pF1->pNextTo); - }else{ - while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } - if( pF2 ){ - pF2->pNextTo = pF1->pNextTo; - } - } - } - sqlite3DeleteTable(db, p); - } - db->flags |= SQLITE_InternChanges; -} - -/* -** Given a token, return a string that consists of the text of that -** token with any quotations removed. Space to hold the returned string -** is obtained from sqliteMalloc() and must be freed by the calling -** function. -** -** Tokens are really just pointers into the original SQL text and so -** are not \000 terminated and are not persistent. The returned string -** is \000 terminated and is persistent. -*/ -char *sqlite3NameFromToken(Token *pName){ - char *zName; - if( pName ){ - zName = sqliteStrNDup(pName->z, pName->n); - sqlite3Dequote(zName); - }else{ - zName = 0; - } - return zName; -} - -/* -** Open the sqlite_master table stored in database number iDb for -** writing. The table is opened using cursor 0. -*/ -void sqlite3OpenMasterTable(Vdbe *v, int iDb){ - sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenWrite, 0, MASTER_ROOT); - sqlite3VdbeAddOp(v, OP_SetNumColumns, 0, 5); /* sqlite_master has 5 columns */ -} - -/* -** The token *pName contains the name of a database (either "main" or -** "temp" or the name of an attached db). This routine returns the -** index of the named database in db->aDb[], or -1 if the named db -** does not exist. -*/ -int findDb(sqlite3 *db, Token *pName){ - int i; - Db *pDb; - for(pDb=db->aDb, i=0; i<db->nDb; i++, pDb++){ - if( pName->n==strlen(pDb->zName) && - 0==sqlite3StrNICmp(pDb->zName, pName->z, pName->n) ){ - return i; - } - } - return -1; -} - -/* The table or view or trigger name is passed to this routine via tokens -** pName1 and pName2. If the table name was fully qualified, for example: -** -** CREATE TABLE xxx.yyy (...); -** -** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if -** the table name is not fully qualified, i.e.: -** -** CREATE TABLE yyy(...); -** -** Then pName1 is set to "yyy" and pName2 is "". -** -** This routine sets the *ppUnqual pointer to point at the token (pName1 or -** pName2) that stores the unqualified table name. The index of the -** database "xxx" is returned. -*/ -int sqlite3TwoPartName( - Parse *pParse, /* Parsing and code generating context */ - Token *pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */ - Token *pName2, /* The "yyy" in the name "xxx.yyy" */ - Token **pUnqual /* Write the unqualified object name here */ -){ - int iDb; /* Database holding the object */ - sqlite3 *db = pParse->db; - - if( pName2 && pName2->n>0 ){ - assert( !db->init.busy ); - *pUnqual = pName2; - iDb = findDb(db, pName1); - if( iDb<0 ){ - sqlite3ErrorMsg(pParse, "unknown database %T", pName1); - pParse->nErr++; - return -1; - } - }else{ - assert( db->init.iDb==0 || db->init.busy ); - iDb = db->init.iDb; - *pUnqual = pName1; - } - return iDb; -} - -/* -** This routine is used to check if the UTF-8 string zName is a legal -** unqualified name for a new schema object (table, index, view or -** trigger). All names are legal except those that begin with the string -** "sqlite_" (in upper, lower or mixed case). This portion of the namespace -** is reserved for internal use. -*/ -int sqlite3CheckObjectName(Parse *pParse, const char *zName){ - if( !pParse->db->init.busy && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -/* -** Begin constructing a new table representation in memory. This is -** the first of several action routines that get called in response -** to a CREATE TABLE statement. In particular, this routine is called -** after seeing tokens "CREATE" and "TABLE" and the table name. The -** pStart token is the CREATE and pName is the table name. The isTemp -** flag is true if the table should be stored in the auxiliary database -** file instead of in the main database file. This is normally the case -** when the "TEMP" or "TEMPORARY" keyword occurs in between -** CREATE and TABLE. -** -** The new table record is initialized and put in pParse->pNewTable. -** As more of the CREATE TABLE statement is parsed, additional action -** routines will be called to add more information to this record. -** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine -** is called to complete the construction of the new table record. -*/ -void sqlite3StartTable( - Parse *pParse, /* Parser context */ - Token *pStart, /* The "CREATE" token */ - Token *pName1, /* First part of the name of the table or view */ - Token *pName2, /* Second part of the name of the table or view */ - int isTemp, /* True if this is a TEMP table */ - int isView /* True if this is a VIEW */ -){ - Table *pTable; - Index *pIdx; - char *zName; - sqlite3 *db = pParse->db; - Vdbe *v; - int iDb; /* Database number to create the table in */ - Token *pName; /* Unqualified name of the table to create */ - - /* The table or view name to create is passed to this routine via tokens - ** pName1 and pName2. If the table name was fully qualified, for example: - ** - ** CREATE TABLE xxx.yyy (...); - ** - ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if - ** the table name is not fully qualified, i.e.: - ** - ** CREATE TABLE yyy(...); - ** - ** Then pName1 is set to "yyy" and pName2 is "". - ** - ** The call below sets the pName pointer to point at the token (pName1 or - ** pName2) that stores the unqualified table name. The variable iDb is - ** set to the index of the database that the table or view is to be - ** created in. - */ - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ) return; - if( isTemp && iDb>1 ){ - /* If creating a temp table, the name may not be qualified */ - sqlite3ErrorMsg(pParse, "temporary table name must be unqualified"); - pParse->nErr++; - return; - } - if( isTemp ) iDb = 1; - - pParse->sNameToken = *pName; - zName = sqlite3NameFromToken(pName); - if( zName==0 ) return; - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - sqliteFree(zName); - return; - } - if( db->init.iDb==1 ) isTemp = 1; -#ifndef SQLITE_OMIT_AUTHORIZATION - assert( (isTemp & 1)==isTemp ); - { - int code; - char *zDb = db->aDb[iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ - sqliteFree(zName); - return; - } - if( isView ){ - if( isTemp ){ - code = SQLITE_CREATE_TEMP_VIEW; - }else{ - code = SQLITE_CREATE_VIEW; - } - }else{ - if( isTemp ){ - code = SQLITE_CREATE_TEMP_TABLE; - }else{ - code = SQLITE_CREATE_TABLE; - } - } - if( sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){ - sqliteFree(zName); - return; - } - } -#endif - - /* Make sure the new table name does not collide with an existing - ** index or table name in the same database. Issue an error message if - ** it does. - */ - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) return; - pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName); - if( pTable ){ - sqlite3ErrorMsg(pParse, "table %T already exists", pName); - sqliteFree(zName); - return; - } - if( (pIdx = sqlite3FindIndex(db, zName, 0))!=0 && - ( iDb==0 || !db->init.busy) ){ - sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); - sqliteFree(zName); - return; - } - pTable = sqliteMalloc( sizeof(Table) ); - if( pTable==0 ){ - pParse->rc = SQLITE_NOMEM; - pParse->nErr++; - sqliteFree(zName); - return; - } - pTable->zName = zName; - pTable->nCol = 0; - pTable->aCol = 0; - pTable->iPKey = -1; - pTable->pIndex = 0; - pTable->iDb = iDb; - if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable); - pParse->pNewTable = pTable; - - /* Begin generating the code that will insert the table record into - ** the SQLITE_MASTER table. Note in particular that we must go ahead - ** and allocate the record number for the table entry now. Before any - ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause - ** indices to be created and the table record must come before the - ** indices. Hence, the record number for the table must be allocated - ** now. - */ - if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ - sqlite3BeginWriteOperation(pParse, 0, iDb); - /* Every time a new table is created the file-format - ** and encoding meta-values are set in the database, in - ** case this is the first table created. - */ - sqlite3VdbeAddOp(v, OP_Integer, db->file_format, 0); - sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1); - sqlite3VdbeAddOp(v, OP_Integer, db->enc, 0); - sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 4); - - sqlite3OpenMasterTable(v, iDb); - sqlite3VdbeAddOp(v, OP_NewRecno, 0, 0); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0); - } -} - -/* -** Add a new column to the table currently being constructed. -** -** The parser calls this routine once for each column declaration -** in a CREATE TABLE statement. sqlite3StartTable() gets called -** first to get things going. Then this routine is called for each -** column. -*/ -void sqlite3AddColumn(Parse *pParse, Token *pName){ - Table *p; - int i; - char *z; - Column *pCol; - if( (p = pParse->pNewTable)==0 ) return; - z = sqlite3NameFromToken(pName); - if( z==0 ) return; - for(i=0; i<p->nCol; i++){ - if( sqlite3StrICmp(z, p->aCol[i].zName)==0 ){ - sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); - sqliteFree(z); - return; - } - } - if( (p->nCol & 0x7)==0 ){ - Column *aNew; - aNew = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); - if( aNew==0 ) return; - p->aCol = aNew; - } - pCol = &p->aCol[p->nCol]; - memset(pCol, 0, sizeof(p->aCol[0])); - pCol->zName = z; - - /* If there is no type specified, columns have the default affinity - ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will - ** be called next to set pCol->affinity correctly. - */ - pCol->affinity = SQLITE_AFF_NONE; - pCol->pColl = pParse->db->pDfltColl; - p->nCol++; -} - -/* -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. A "NOT NULL" constraint has -** been seen on a column. This routine sets the notNull flag on -** the column currently under construction. -*/ -void sqlite3AddNotNull(Parse *pParse, int onError){ - Table *p; - int i; - if( (p = pParse->pNewTable)==0 ) return; - i = p->nCol-1; - if( i>=0 ) p->aCol[i].notNull = onError; -} - -/* -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. The pFirst token is the first -** token in the sequence of tokens that describe the type of the -** column currently under construction. pLast is the last token -** in the sequence. Use this information to construct a string -** that contains the typename of the column and store that string -** in zType. -*/ -void sqlite3AddColumnType(Parse *pParse, Token *pFirst, Token *pLast){ - Table *p; - int i, j; - int n; - char *z, **pz; - Column *pCol; - if( (p = pParse->pNewTable)==0 ) return; - i = p->nCol-1; - if( i<0 ) return; - pCol = &p->aCol[i]; - pz = &pCol->zType; - n = pLast->n + (pLast->z - pFirst->z); - assert( pCol->zType==0 ); - z = pCol->zType = sqlite3MPrintf("%.*s", n, pFirst->z); - if( z==0 ) return; - for(i=j=0; z[i]; i++){ - int c = z[i]; - if( isspace(c) ) continue; - z[j++] = c; - } - z[j] = 0; - pCol->affinity = sqlite3AffinityType(z, n); -} - -/* -** The given token is the default value for the last column added to -** the table currently under construction. If "minusFlag" is true, it -** means the value token was preceded by a minus sign. -** -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. -*/ -void sqlite3AddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ - Table *p; - int i; - char *z; - if( (p = pParse->pNewTable)==0 ) return; - i = p->nCol-1; - if( i<0 ) return; - assert( p->aCol[i].zDflt==0 ); - z = p->aCol[i].zDflt = sqlite3MPrintf("%s%T", minusFlag ? "-" : "", pVal); - sqlite3Dequote(z); -} - -/* -** Designate the PRIMARY KEY for the table. pList is a list of names -** of columns that form the primary key. If pList is NULL, then the -** most recently added column of the table is the primary key. -** -** A table can have at most one primary key. If the table already has -** a primary key (and this is the second primary key) then create an -** error. -** -** If the PRIMARY KEY is on a single column whose datatype is INTEGER, -** then we will try to use that column as the row id. (Exception: -** For backwards compatibility with older databases, do not do this -** if the file format version number is less than 1.) Set the Table.iPKey -** field of the table under construction to be the index of the -** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is -** no INTEGER PRIMARY KEY. -** -** If the key is not an INTEGER PRIMARY KEY, then create a unique -** index for the key. No index is created for INTEGER PRIMARY KEYs. -*/ -void sqlite3AddPrimaryKey(Parse *pParse, ExprList *pList, int onError){ - Table *pTab = pParse->pNewTable; - char *zType = 0; - int iCol = -1, i; - if( pTab==0 ) goto primary_key_exit; - if( pTab->hasPrimKey ){ - sqlite3ErrorMsg(pParse, - "table \"%s\" has more than one primary key", pTab->zName); - goto primary_key_exit; - } - pTab->hasPrimKey = 1; - if( pList==0 ){ - iCol = pTab->nCol - 1; - pTab->aCol[iCol].isPrimKey = 1; - }else{ - for(i=0; i<pList->nExpr; i++){ - for(iCol=0; iCol<pTab->nCol; iCol++){ - if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){ - break; - } - } - if( iCol<pTab->nCol ) pTab->aCol[iCol].isPrimKey = 1; - } - if( pList->nExpr>1 ) iCol = -1; - } - if( iCol>=0 && iCol<pTab->nCol ){ - zType = pTab->aCol[iCol].zType; - } - if( zType && sqlite3StrICmp(zType, "INTEGER")==0 ){ - pTab->iPKey = iCol; - pTab->keyConf = onError; - }else{ - sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0); - pList = 0; - } - -primary_key_exit: - sqlite3ExprListDelete(pList); - return; -} - -/* -** Set the collation function of the most recently parsed table column -** to the CollSeq given. -*/ -void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){ - Table *p; - Index *pIdx; - CollSeq *pColl; - int i; - - if( (p = pParse->pNewTable)==0 ) return; - i = p->nCol-1; - - pColl = sqlite3LocateCollSeq(pParse, zType, nType); - p->aCol[i].pColl = pColl; - - /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>", - ** then an index may have been created on this column before the - ** collation type was added. Correct this if it is the case. - */ - for(pIdx = p->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->nColumn==1 ); - if( pIdx->aiColumn[0]==i ) pIdx->keyInfo.aColl[0] = pColl; - } -} - -/* -** Locate and return an entry from the db.aCollSeq hash table. If the entry -** specified by zName and nName is not found and parameter 'create' is -** true, then create a new entry. Otherwise return NULL. -** -** Each pointer stored in the sqlite3.aCollSeq hash table contains an -** array of three CollSeq structures. The first is the collation sequence -** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be. -** -** Stored immediately after the three collation sequences is a copy of -** the collation sequence name. A pointer to this string is stored in -** each collation sequence structure. -*/ -static CollSeq * findCollSeqEntry( - sqlite3 *db, - const char *zName, - int nName, - int create -){ - CollSeq *pColl; - if( nName<0 ) nName = strlen(zName); - pColl = sqlite3HashFind(&db->aCollSeq, zName, nName); - - if( 0==pColl && create ){ - pColl = sqliteMalloc( 3*sizeof(*pColl) + nName + 1 ); - if( pColl ){ - pColl[0].zName = (char*)&pColl[3]; - pColl[0].enc = SQLITE_UTF8; - pColl[1].zName = (char*)&pColl[3]; - pColl[1].enc = SQLITE_UTF16LE; - pColl[2].zName = (char*)&pColl[3]; - pColl[2].enc = SQLITE_UTF16BE; - memcpy(pColl[0].zName, zName, nName); - pColl[0].zName[nName] = 0; - sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); - } - } - return pColl; -} - -/* -** Parameter zName points to a UTF-8 encoded string nName bytes long. -** Return the CollSeq* pointer for the collation sequence named zName -** for the encoding 'enc' from the database 'db'. -** -** If the entry specified is not found and 'create' is true, then create a -** new entry. Otherwise return NULL. -*/ -CollSeq *sqlite3FindCollSeq( - sqlite3 *db, - u8 enc, - const char *zName, - int nName, - int create -){ - CollSeq *pColl = findCollSeqEntry(db, zName, nName, create); - assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE ); - if( pColl ) pColl += enc-1; - return pColl; -} - -/* -** Invoke the 'collation needed' callback to request a collation sequence -** in the database text encoding of name zName, length nName. -** If the collation sequence -*/ -static void callCollNeeded(sqlite3 *db, const char *zName, int nName){ - assert( !db->xCollNeeded || !db->xCollNeeded16 ); - if( nName<0 ) nName = strlen(zName); - if( db->xCollNeeded ){ - char *zExternal = sqliteStrNDup(zName, nName); - if( !zExternal ) return; - db->xCollNeeded(db->pCollNeededArg, db, (int)db->enc, zExternal); - sqliteFree(zExternal); - } - if( db->xCollNeeded16 ){ - char const *zExternal; - sqlite3_value *pTmp = sqlite3GetTransientValue(db); - sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF8, SQLITE_STATIC); - zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE); - if( !zExternal ) return; - db->xCollNeeded16(db->pCollNeededArg, db, (int)db->enc, zExternal); - } -} - -/* -** This routine is called if the collation factory fails to deliver a -** collation function in the best encoding but there may be other versions -** of this collation function (for other text encodings) available. Use one -** of these instead if they exist. Avoid a UTF-8 <-> UTF-16 conversion if -** possible. -*/ -static int synthCollSeq(Parse *pParse, CollSeq *pColl){ - CollSeq *pColl2; - char *z = pColl->zName; - int n = strlen(z); - sqlite3 *db = pParse->db; - int i; - static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 }; - for(i=0; i<3; i++){ - pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, n, 0); - if( pColl2->xCmp!=0 ){ - memcpy(pColl, pColl2, sizeof(CollSeq)); - return SQLITE_OK; - } - } - if( pParse->nErr==0 ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", n, z); - } - pParse->nErr++; - return SQLITE_ERROR; -} - -/* -** This routine is called on a collation sequence before it is used to -** check that it is defined. An undefined collation sequence exists when -** a database is loaded that contains references to collation sequences -** that have not been defined by sqlite3_create_collation() etc. -** -** If required, this routine calls the 'collation needed' callback to -** request a definition of the collating sequence. If this doesn't work, -** an equivalent collating sequence that uses a text encoding different -** from the main database is substituted, if one is available. -*/ -int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ - if( pColl && !pColl->xCmp ){ - /* No collation sequence of this type for this encoding is registered. - ** Call the collation factory to see if it can supply us with one. - */ - callCollNeeded(pParse->db, pColl->zName, strlen(pColl->zName)); - if( !pColl->xCmp && synthCollSeq(pParse, pColl) ){ - return SQLITE_ERROR; - } - } - return SQLITE_OK; -} - -/* -** Call sqlite3CheckCollSeq() for all collating sequences in an index, -** in order to verify that all the necessary collating sequences are -** loaded. -*/ -int sqlite3CheckIndexCollSeq(Parse *pParse, Index *pIdx){ - if( pIdx ){ - int i; - for(i=0; i<pIdx->nColumn; i++){ - if( sqlite3CheckCollSeq(pParse, pIdx->keyInfo.aColl[i]) ){ - return SQLITE_ERROR; - } - } - } - return SQLITE_OK; -} - -/* -** This function returns the collation sequence for database native text -** encoding identified by the string zName, length nName. -** -** If the requested collation sequence is not available, or not available -** in the database native encoding, the collation factory is invoked to -** request it. If the collation factory does not supply such a sequence, -** and the sequence is available in another text encoding, then that is -** returned instead. -** -** If no versions of the requested collations sequence are available, or -** another error occurs, NULL is returned and an error message written into -** pParse. -*/ -CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){ - u8 enc = pParse->db->enc; - u8 initbusy = pParse->db->init.busy; - CollSeq *pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, initbusy); - if( nName<0 ) nName = strlen(zName); - if( !initbusy && (!pColl || !pColl->xCmp) ){ - /* No collation sequence of this type for this encoding is registered. - ** Call the collation factory to see if it can supply us with one. - */ - callCollNeeded(pParse->db, zName, nName); - pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, 0); - if( pColl && !pColl->xCmp ){ - /* There may be a version of the collation sequence that requires - ** translation between encodings. Search for it with synthCollSeq(). - */ - if( synthCollSeq(pParse, pColl) ){ - return 0; - } - } - } - - /* If nothing has been found, write the error message into pParse */ - if( !initbusy && (!pColl || !pColl->xCmp) ){ - if( pParse->nErr==0 ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", nName, zName); - } - pColl = 0; - } - return pColl; -} - - - -/* -** Scan the column type name zType (length nType) and return the -** associated affinity type. -*/ -char sqlite3AffinityType(const char *zType, int nType){ - int n, i; - static const struct { - const char *zSub; /* Keywords substring to search for */ - char nSub; /* length of zSub */ - char affinity; /* Affinity to return if it matches */ - } substrings[] = { - {"INT", 3, SQLITE_AFF_INTEGER}, - {"CHAR", 4, SQLITE_AFF_TEXT}, - {"CLOB", 4, SQLITE_AFF_TEXT}, - {"TEXT", 4, SQLITE_AFF_TEXT}, - {"BLOB", 4, SQLITE_AFF_NONE}, - }; - - if( nType==0 ){ - return SQLITE_AFF_NONE; - } - for(i=0; i<sizeof(substrings)/sizeof(substrings[0]); i++){ - int c1 = substrings[i].zSub[0]; - int c2 = tolower(c1); - int limit = nType - substrings[i].nSub; - const char *z = substrings[i].zSub; - for(n=0; n<=limit; n++){ - int c = zType[n]; - if( (c==c1 || c==c2) - && 0==sqlite3StrNICmp(&zType[n], z, substrings[i].nSub) ){ - return substrings[i].affinity; - } - } - } - return SQLITE_AFF_NUMERIC; -} - -/* -** Generate code that will increment the schema cookie. -** -** The schema cookie is used to determine when the schema for the -** database changes. After each schema change, the cookie value -** changes. When a process first reads the schema it records the -** cookie. Thereafter, whenever it goes to access the database, -** it checks the cookie to make sure the schema has not changed -** since it was last read. -** -** This plan is not completely bullet-proof. It is possible for -** the schema to change multiple times and for the cookie to be -** set back to prior value. But schema changes are infrequent -** and the probability of hitting the same cookie value is only -** 1 chance in 2^32. So we're safe enough. -*/ -void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){ - sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].schema_cookie+1, 0); - sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0); -} - -/* -** Measure the number of characters needed to output the given -** identifier. The number returned includes any quotes used -** but does not include the null terminator. -** -** The estimate is conservative. It might be larger that what is -** really needed. -*/ -static int identLength(const char *z){ - int n; - for(n=0; *z; n++, z++){ - if( *z=='"' ){ n++; } - } - return n + 2; -} - -/* -** Write an identifier onto the end of the given string. Add -** quote characters as needed. -*/ -static void identPut(char *z, int *pIdx, char *zSignedIdent){ - unsigned char *zIdent = (unsigned char*)zSignedIdent; - int i, j, needQuote; - i = *pIdx; - for(j=0; zIdent[j]; j++){ - if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break; - } - needQuote = zIdent[j]!=0 || isdigit(zIdent[0]) - || sqlite3KeywordCode(zIdent, j)!=TK_ID; - if( needQuote ) z[i++] = '"'; - for(j=0; zIdent[j]; j++){ - z[i++] = zIdent[j]; - if( zIdent[j]=='"' ) z[i++] = '"'; - } - if( needQuote ) z[i++] = '"'; - z[i] = 0; - *pIdx = i; -} - -/* -** Generate a CREATE TABLE statement appropriate for the given -** table. Memory to hold the text of the statement is obtained -** from sqliteMalloc() and must be freed by the calling function. -*/ -static char *createTableStmt(Table *p){ - int i, k, n; - char *zStmt; - char *zSep, *zSep2, *zEnd, *z; - Column *pCol; - n = 0; - for(pCol = p->aCol, i=0; i<p->nCol; i++, pCol++){ - n += identLength(pCol->zName); - z = pCol->zType; - if( z ){ - n += (strlen(z) + 1); - } - } - n += identLength(p->zName); - if( n<50 ){ - zSep = ""; - zSep2 = ","; - zEnd = ")"; - }else{ - zSep = "\n "; - zSep2 = ",\n "; - zEnd = "\n)"; - } - n += 35 + 6*p->nCol; - zStmt = sqliteMallocRaw( n ); - if( zStmt==0 ) return 0; - strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE "); - k = strlen(zStmt); - identPut(zStmt, &k, p->zName); - zStmt[k++] = '('; - for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){ - strcpy(&zStmt[k], zSep); - k += strlen(&zStmt[k]); - zSep = zSep2; - identPut(zStmt, &k, pCol->zName); - if( (z = pCol->zType)!=0 ){ - zStmt[k++] = ' '; - strcpy(&zStmt[k], z); - k += strlen(z); - } - } - strcpy(&zStmt[k], zEnd); - return zStmt; -} - -/* -** This routine is called to report the final ")" that terminates -** a CREATE TABLE statement. -** -** The table structure that other action routines have been building -** is added to the internal hash tables, assuming no errors have -** occurred. -** -** An entry for the table is made in the master table on disk, unless -** this is a temporary table or db->init.busy==1. When db->init.busy==1 -** it means we are reading the sqlite_master table because we just -** connected to the database or because the sqlite_master table has -** recently changes, so the entry for this table already exists in -** the sqlite_master table. We do not want to create it again. -** -** If the pSelect argument is not NULL, it means that this routine -** was called to create a table generated from a -** "CREATE TABLE ... AS SELECT ..." statement. The column names of -** the new table will match the result set of the SELECT. -*/ -void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){ - Table *p; - sqlite3 *db = pParse->db; - - if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3_malloc_failed ) return; - p = pParse->pNewTable; - if( p==0 ) return; - - assert( !db->init.busy || !pSelect ); - - /* If the db->init.busy is 1 it means we are reading the SQL off the - ** "sqlite_master" or "sqlite_temp_master" table on the disk. - ** So do not write to the disk again. Extract the root page number - ** for the table from the db->init.newTnum field. (The page number - ** should have been put there by the sqliteOpenCb routine.) - */ - if( db->init.busy ){ - p->tnum = db->init.newTnum; - } - - /* If not initializing, then create a record for the new table - ** in the SQLITE_MASTER table of the database. The record number - ** for the new table entry should already be on the stack. - ** - ** If this is a TEMPORARY table, write the entry into the auxiliary - ** file instead of into the main database file. - */ - if( !db->init.busy ){ - int n; - Vdbe *v; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - - if( p->pSelect==0 ){ - /* A regular table */ - sqlite3VdbeAddOp(v, OP_CreateTable, p->iDb, 0); - }else{ - /* A view */ - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - } - - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - - /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT - ** statement to populate the new table. The root-page number for the - ** new table is on the top of the vdbe stack. - ** - ** Once the SELECT has been coded by sqlite3Select(), it is in a - ** suitable state to query for the column names and types to be used - ** by the new table. - */ - if( pSelect ){ - Table *pSelTab; - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3VdbeAddOp(v, OP_Integer, p->iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0); - pParse->nTab = 2; - sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0); - sqlite3VdbeAddOp(v, OP_Close, 1, 0); - if( pParse->nErr==0 ){ - pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect); - if( pSelTab==0 ) return; - assert( p->aCol==0 ); - p->nCol = pSelTab->nCol; - p->aCol = pSelTab->aCol; - pSelTab->nCol = 0; - pSelTab->aCol = 0; - sqlite3DeleteTable(0, pSelTab); - } - } - - sqlite3OpenMasterTable(v, p->iDb); - - sqlite3VdbeOp3(v, OP_String8, 0, 0, p->pSelect==0?"table":"view",P3_STATIC); - sqlite3VdbeOp3(v, OP_String8, 0, 0, p->zName, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, p->zName, 0); - sqlite3VdbeAddOp(v, OP_Pull, 3, 0); - - if( pSelect ){ - char *z = createTableStmt(p); - n = z ? strlen(z) : 0; - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeChangeP3(v, -1, z, n); - sqliteFree(z); - }else{ - if( p->pSelect ){ - sqlite3VdbeOp3(v, OP_String8, 0, 0, "CREATE VIEW ", P3_STATIC); - }else{ - sqlite3VdbeOp3(v, OP_String8, 0, 0, "CREATE TABLE ", P3_STATIC); - } - assert( pEnd!=0 ); - n = Addr(pEnd->z) - Addr(pParse->sNameToken.z) + 1; - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeChangeP3(v, -1, pParse->sNameToken.z, n); - sqlite3VdbeAddOp(v, OP_Concat, 0, 0); - } - sqlite3VdbeOp3(v, OP_MakeRecord, 5, 0, "tttit", P3_STATIC); - sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0); - sqlite3ChangeCookie(db, v, p->iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeOp3(v, OP_ParseSchema, p->iDb, 0, - sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC); - } - - /* Add the table to the in-memory representation of the database. - */ - if( db->init.busy && pParse->nErr==0 ){ - Table *pOld; - FKey *pFKey; - Db *pDb = &db->aDb[p->iDb]; - pOld = sqlite3HashInsert(&pDb->tblHash, p->zName, strlen(p->zName)+1, p); - if( pOld ){ - assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ - return; - } - for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){ - int nTo = strlen(pFKey->zTo) + 1; - pFKey->pNextTo = sqlite3HashFind(&pDb->aFKey, pFKey->zTo, nTo); - sqlite3HashInsert(&pDb->aFKey, pFKey->zTo, nTo, pFKey); - } - pParse->pNewTable = 0; - db->nTable++; - db->flags |= SQLITE_InternChanges; - } -} - -/* -** The parser calls this routine in order to create a new VIEW -*/ -void sqlite3CreateView( - Parse *pParse, /* The parsing context */ - Token *pBegin, /* The CREATE token that begins the statement */ - Token *pName1, /* The token that holds the name of the view */ - Token *pName2, /* The token that holds the name of the view */ - Select *pSelect, /* A SELECT statement that will become the new view */ - int isTemp /* TRUE for a TEMPORARY view */ -){ - Table *p; - int n; - const unsigned char *z; - Token sEnd; - DbFixer sFix; - Token *pName; - - sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1); - p = pParse->pNewTable; - if( p==0 || pParse->nErr ){ - sqlite3SelectDelete(pSelect); - return; - } - sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName) - && sqlite3FixSelect(&sFix, pSelect) - ){ - sqlite3SelectDelete(pSelect); - return; - } - - /* Make a copy of the entire SELECT statement that defines the view. - ** This will force all the Expr.token.z values to be dynamically - ** allocated rather than point to the input string - which means that - ** they will persist after the current sqlite3_exec() call returns. - */ - p->pSelect = sqlite3SelectDup(pSelect); - sqlite3SelectDelete(pSelect); - if( !pParse->db->init.busy ){ - sqlite3ViewGetColumnNames(pParse, p); - } - - /* Locate the end of the CREATE VIEW statement. Make sEnd point to - ** the end. - */ - sEnd = pParse->sLastToken; - if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){ - sEnd.z += sEnd.n; - } - sEnd.n = 0; - n = sEnd.z - pBegin->z; - z = (const unsigned char*)pBegin->z; - while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; } - sEnd.z = &z[n-1]; - sEnd.n = 1; - - /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ - sqlite3EndTable(pParse, &sEnd, 0); - return; -} - -/* -** The Table structure pTable is really a VIEW. Fill in the names of -** the columns of the view in the pTable structure. Return the number -** of errors. If an error is seen leave an error message in pParse->zErrMsg. -*/ -int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - ExprList *pEList; - Select *pSel; - Table *pSelTab; - int nErr = 0; - - assert( pTable ); - - /* A positive nCol means the columns names for this view are - ** already known. - */ - if( pTable->nCol>0 ) return 0; - - /* A negative nCol is a special marker meaning that we are currently - ** trying to compute the column names. If we enter this routine with - ** a negative nCol, it means two or more views form a loop, like this: - ** - ** CREATE VIEW one AS SELECT * FROM two; - ** CREATE VIEW two AS SELECT * FROM one; - ** - ** Actually, this error is caught previously and so the following test - ** should always fail. But we will leave it in place just to be safe. - */ - if( pTable->nCol<0 ){ - sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable->zName); - return 1; - } - - /* If we get this far, it means we need to compute the table names. - */ - assert( pTable->pSelect ); /* If nCol==0, then pTable must be a VIEW */ - pSel = pTable->pSelect; - - /* Note that the call to sqlite3ResultSetOfSelect() will expand any - ** "*" elements in this list. But we will need to restore the list - ** back to its original configuration afterwards, so we save a copy of - ** the original in pEList. - */ - pEList = pSel->pEList; - pSel->pEList = sqlite3ExprListDup(pEList); - if( pSel->pEList==0 ){ - pSel->pEList = pEList; - return 1; /* Malloc failed */ - } - pTable->nCol = -1; - pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel); - if( pSelTab ){ - assert( pTable->aCol==0 ); - pTable->nCol = pSelTab->nCol; - pTable->aCol = pSelTab->aCol; - pSelTab->nCol = 0; - pSelTab->aCol = 0; - sqlite3DeleteTable(0, pSelTab); - DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews); - }else{ - pTable->nCol = 0; - nErr++; - } - sqlite3SelectUnbind(pSel); - sqlite3ExprListDelete(pSel->pEList); - pSel->pEList = pEList; - return nErr; -} - -/* -** Clear the column names from every VIEW in database idx. -*/ -static void sqliteViewResetAll(sqlite3 *db, int idx){ - HashElem *i; - if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; - for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){ - Table *pTab = sqliteHashData(i); - if( pTab->pSelect ){ - sqliteResetColumnNames(pTab); - } - } - DbClearProperty(db, idx, DB_UnresetViews); -} - -/* -** This routine is called to do the work of a DROP TABLE statement. -** pName is the name of the table to be dropped. -*/ -void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView){ - Table *pTab; - Vdbe *v; - int base; - sqlite3 *db = pParse->db; - int iDb; - - if( pParse->nErr || sqlite3_malloc_failed ) goto exit_drop_table; - assert( pName->nSrc==1 ); - pTab = sqlite3LocateTable(pParse, pName->a[0].zName, pName->a[0].zDatabase); - - if( pTab==0 ) goto exit_drop_table; - iDb = pTab->iDb; - assert( iDb>=0 && iDb<db->nDb ); -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code; - const char *zTab = SCHEMA_TABLE(pTab->iDb); - const char *zDb = db->aDb[pTab->iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ - goto exit_drop_table; - } - if( isView ){ - if( iDb==1 ){ - code = SQLITE_DROP_TEMP_VIEW; - }else{ - code = SQLITE_DROP_VIEW; - } - }else{ - if( iDb==1 ){ - code = SQLITE_DROP_TEMP_TABLE; - }else{ - code = SQLITE_DROP_TABLE; - } - } - if( sqlite3AuthCheck(pParse, code, pTab->zName, 0, zDb) ){ - goto exit_drop_table; - } - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ - goto exit_drop_table; - } - } -#endif - if( pTab->readOnly ){ - sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); - pParse->nErr++; - goto exit_drop_table; - } - if( isView && pTab->pSelect==0 ){ - sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); - goto exit_drop_table; - } - if( !isView && pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); - goto exit_drop_table; - } - - /* Generate code to remove the table from the master table - ** on disk. - */ - v = sqlite3GetVdbe(pParse); - if( v ){ - static const VdbeOpList dropTable[] = { - { OP_Rewind, 0, ADDR(13), 0}, - { OP_String8, 0, 0, 0}, /* 1 */ - { OP_MemStore, 1, 1, 0}, - { OP_MemLoad, 1, 0, 0}, /* 3 */ - { OP_Column, 0, 2, 0}, /* sqlite_master.tbl_name */ - { OP_Ne, 0, ADDR(12), 0}, - { OP_String8, 0, 0, "trigger"}, - { OP_Column, 0, 2, 0}, /* sqlite_master.type */ - { OP_Eq, 0, ADDR(12), 0}, - { OP_Delete, 0, 0, 0}, - { OP_Rewind, 0, ADDR(13), 0}, - { OP_Goto, 0, ADDR(3), 0}, - { OP_Next, 0, ADDR(3), 0}, /* 12 */ - }; - Index *pIdx; - Trigger *pTrigger; - sqlite3BeginWriteOperation(pParse, 0, pTab->iDb); - - /* Drop all triggers associated with the table being dropped. Code - ** is generated to remove entries from sqlite_master and/or - ** sqlite_temp_master if required. - */ - pTrigger = pTab->pTrigger; - while( pTrigger ){ - assert( pTrigger->iDb==pTab->iDb || pTrigger->iDb==1 ); - sqlite3DropTriggerPtr(pParse, pTrigger, 1); - pTrigger = pTrigger->pNext; - } - - /* Drop all SQLITE_MASTER table and index entries that refer to the - ** table. The program name loops through the master table and deletes - ** every row that refers to a table of the same name as the one being - ** dropped. Triggers are handled seperately because a trigger can be - ** created in the temp database that refers to a table in another - ** database. - */ - sqlite3OpenMasterTable(v, pTab->iDb); - base = sqlite3VdbeAddOpList(v, ArraySize(dropTable), dropTable); - sqlite3VdbeChangeP3(v, base+1, pTab->zName, 0); - sqlite3ChangeCookie(db, v, pTab->iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - if( !isView ){ - sqlite3VdbeAddOp(v, OP_Destroy, pTab->tnum, pTab->iDb); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqlite3VdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb); - } - } - sqlite3VdbeOp3(v, OP_DropTable, pTab->iDb, 0, pTab->zName, 0); - } - sqliteViewResetAll(db, iDb); - -exit_drop_table: - sqlite3SrcListDelete(pName); -} - -/* -** This routine is called to create a new foreign key on the table -** currently under construction. pFromCol determines which columns -** in the current table point to the foreign key. If pFromCol==0 then -** connect the key to the last column inserted. pTo is the name of -** the table referred to. pToCol is a list of tables in the other -** pTo table that the foreign key points to. flags contains all -** information about the conflict resolution algorithms specified -** in the ON DELETE, ON UPDATE and ON INSERT clauses. -** -** An FKey structure is created and added to the table currently -** under construction in the pParse->pNewTable field. The new FKey -** is not linked into db->aFKey at this point - that does not happen -** until sqlite3EndTable(). -** -** The foreign key is set for IMMEDIATE processing. A subsequent call -** to sqlite3DeferForeignKey() might change this to DEFERRED. -*/ -void sqlite3CreateForeignKey( - Parse *pParse, /* Parsing context */ - ExprList *pFromCol, /* Columns in this table that point to other table */ - Token *pTo, /* Name of the other table */ - ExprList *pToCol, /* Columns in the other table */ - int flags /* Conflict resolution algorithms. */ -){ - Table *p = pParse->pNewTable; - int nByte; - int i; - int nCol; - char *z; - FKey *pFKey = 0; - - assert( pTo!=0 ); - if( p==0 || pParse->nErr ) goto fk_end; - if( pFromCol==0 ){ - int iCol = p->nCol-1; - if( iCol<0 ) goto fk_end; - if( pToCol && pToCol->nExpr!=1 ){ - sqlite3ErrorMsg(pParse, "foreign key on %s" - " should reference only one column of table %T", - p->aCol[iCol].zName, pTo); - goto fk_end; - } - nCol = 1; - }else if( pToCol && pToCol->nExpr!=pFromCol->nExpr ){ - sqlite3ErrorMsg(pParse, - "number of columns in foreign key does not match the number of " - "columns in the referenced table"); - goto fk_end; - }else{ - nCol = pFromCol->nExpr; - } - nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1; - if( pToCol ){ - for(i=0; i<pToCol->nExpr; i++){ - nByte += strlen(pToCol->a[i].zName) + 1; - } - } - pFKey = sqliteMalloc( nByte ); - if( pFKey==0 ) goto fk_end; - pFKey->pFrom = p; - pFKey->pNextFrom = p->pFKey; - z = (char*)&pFKey[1]; - pFKey->aCol = (struct sColMap*)z; - z += sizeof(struct sColMap)*nCol; - pFKey->zTo = z; - memcpy(z, pTo->z, pTo->n); - z[pTo->n] = 0; - z += pTo->n+1; - pFKey->pNextTo = 0; - pFKey->nCol = nCol; - if( pFromCol==0 ){ - pFKey->aCol[0].iFrom = p->nCol-1; - }else{ - for(i=0; i<nCol; i++){ - int j; - for(j=0; j<p->nCol; j++){ - if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){ - pFKey->aCol[i].iFrom = j; - break; - } - } - if( j>=p->nCol ){ - sqlite3ErrorMsg(pParse, - "unknown column \"%s\" in foreign key definition", - pFromCol->a[i].zName); - goto fk_end; - } - } - } - if( pToCol ){ - for(i=0; i<nCol; i++){ - int n = strlen(pToCol->a[i].zName); - pFKey->aCol[i].zCol = z; - memcpy(z, pToCol->a[i].zName, n); - z[n] = 0; - z += n+1; - } - } - pFKey->isDeferred = 0; - pFKey->deleteConf = flags & 0xff; - pFKey->updateConf = (flags >> 8 ) & 0xff; - pFKey->insertConf = (flags >> 16 ) & 0xff; - - /* Link the foreign key to the table as the last step. - */ - p->pFKey = pFKey; - pFKey = 0; - -fk_end: - sqliteFree(pFKey); - sqlite3ExprListDelete(pFromCol); - sqlite3ExprListDelete(pToCol); -} - -/* -** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED -** clause is seen as part of a foreign key definition. The isDeferred -** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE. -** The behavior of the most recently created foreign key is adjusted -** accordingly. -*/ -void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ - Table *pTab; - FKey *pFKey; - if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; - pFKey->isDeferred = isDeferred; -} - -/* -** Create a new index for an SQL table. pIndex is the name of the index -** and pTable is the name of the table that is to be indexed. Both will -** be NULL for a primary key or an index that is created to satisfy a -** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable -** as the table to be indexed. pParse->pNewTable is a table that is -** currently being constructed by a CREATE TABLE statement. -** -** pList is a list of columns to be indexed. pList will be NULL if this -** is a primary key or unique-constraint on the most recent column added -** to the table currently under construction. -*/ -void sqlite3CreateIndex( - Parse *pParse, /* All information about this parse */ - Token *pName1, /* First part of index name. May be NULL */ - Token *pName2, /* Second part of index name. May be NULL */ - SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */ - ExprList *pList, /* A list of columns to be indexed */ - int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ - Token *pEnd /* The ")" that closes the CREATE INDEX statement */ -){ - Table *pTab = 0; /* Table to be indexed */ - Index *pIndex = 0; /* The index to be created */ - char *zName = 0; - int i, j; - Token nullId; /* Fake token for an empty ID list */ - DbFixer sFix; /* For assigning database names to pTable */ - int isTemp; /* True for a temporary index */ - sqlite3 *db = pParse->db; - - int iDb; /* Index of the database that is being written */ - Token *pName = 0; /* Unqualified name of the index to create */ - - if( pParse->nErr || sqlite3_malloc_failed ) goto exit_create_index; - - /* - ** Find the table that is to be indexed. Return early if not found. - */ - if( pTblName!=0 ){ - - /* Use the two-part index name to determine the database - ** to search for the table. 'Fix' the table name to this db - ** before looking up the table. - */ - assert( pName1 && pName2 ); - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ) goto exit_create_index; - - /* If the index name was unqualified, check if the the table - ** is a temp table. If so, set the database to 1. - */ - pTab = sqlite3SrcListLookup(pParse, pTblName); - if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){ - iDb = 1; - } - - if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) && - sqlite3FixSrcList(&sFix, pTblName) - ){ - goto exit_create_index; - } - pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, - pTblName->a[0].zDatabase); - if( !pTab ) goto exit_create_index; - assert( iDb==pTab->iDb ); - }else{ - assert( pName==0 ); - pTab = pParse->pNewTable; - iDb = pTab->iDb; - } - - if( pTab==0 || pParse->nErr ) goto exit_create_index; - if( pTab->readOnly ){ - sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); - goto exit_create_index; - } - if( pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "views may not be indexed"); - goto exit_create_index; - } - isTemp = pTab->iDb==1; - - /* - ** Find the name of the index. Make sure there is not already another - ** index or table with the same name. - ** - ** Exception: If we are reading the names of permanent indices from the - ** sqlite_master table (because some other process changed the schema) and - ** one of the index names collides with the name of a temporary table or - ** index, then we will continue to process this index. - ** - ** If pName==0 it means that we are - ** dealing with a primary key or UNIQUE constraint. We have to invent our - ** own name. - */ - if( pName ){ - zName = sqlite3NameFromToken(pName); - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index; - if( zName==0 ) goto exit_create_index; - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto exit_create_index; - } - if( !db->init.busy ){ - Index *pISameName; /* Another index with the same name */ - Table *pTSameName; /* A table with same name as the index */ - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index; - if( (pISameName = sqlite3FindIndex(db, zName, db->aDb[iDb].zName))!=0 ){ - sqlite3ErrorMsg(pParse, "index %s already exists", zName); - goto exit_create_index; - } - if( (pTSameName = sqlite3FindTable(db, zName, 0))!=0 ){ - sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); - goto exit_create_index; - } - } - }else if( pName==0 ){ - char zBuf[30]; - int n; - Index *pLoop; - for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} - sprintf(zBuf,"_%d",n); - zName = 0; - sqlite3SetString(&zName, "sqlite_autoindex_", pTab->zName, zBuf, (char*)0); - if( zName==0 ) goto exit_create_index; - } - - /* Check for authorization to create an index. - */ -#ifndef SQLITE_OMIT_AUTHORIZATION - { - const char *zDb = db->aDb[pTab->iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ - goto exit_create_index; - } - i = SQLITE_CREATE_INDEX; - if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, i, zName, pTab->zName, zDb) ){ - goto exit_create_index; - } - } -#endif - - /* If pList==0, it means this routine was called to make a primary - ** key out of the last column added to the table under construction. - ** So create a fake list to simulate this. - */ - if( pList==0 ){ - nullId.z = pTab->aCol[pTab->nCol-1].zName; - nullId.n = strlen(nullId.z); - pList = sqlite3ExprListAppend(0, 0, &nullId); - if( pList==0 ) goto exit_create_index; - } - - /* - ** Allocate the index structure. - */ - pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + 1 + - (sizeof(int) + sizeof(CollSeq*))*pList->nExpr ); - if( pIndex==0 ) goto exit_create_index; - pIndex->aiColumn = (int*)&pIndex->keyInfo.aColl[pList->nExpr]; - pIndex->zName = (char*)&pIndex->aiColumn[pList->nExpr]; - strcpy(pIndex->zName, zName); - pIndex->pTable = pTab; - pIndex->nColumn = pList->nExpr; - pIndex->onError = onError; - pIndex->autoIndex = pName==0; - pIndex->iDb = iDb; - - /* Scan the names of the columns of the table to be indexed and - ** load the column indices into the Index structure. Report an error - ** if any column is not found. - */ - for(i=0; i<pList->nExpr; i++){ - for(j=0; j<pTab->nCol; j++){ - if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[j].zName)==0 ) break; - } - if( j>=pTab->nCol ){ - sqlite3ErrorMsg(pParse, "table %s has no column named %s", - pTab->zName, pList->a[i].zName); - goto exit_create_index; - } - pIndex->aiColumn[i] = j; - if( pList->a[i].pExpr ){ - assert( pList->a[i].pExpr->pColl ); - pIndex->keyInfo.aColl[i] = pList->a[i].pExpr->pColl; - }else{ - pIndex->keyInfo.aColl[i] = pTab->aCol[j].pColl; - } - assert( pIndex->keyInfo.aColl[i] ); - if( !db->init.busy && - sqlite3CheckCollSeq(pParse, pIndex->keyInfo.aColl[i]) - ){ - goto exit_create_index; - } - } - pIndex->keyInfo.nField = pList->nExpr; - - if( pTab==pParse->pNewTable ){ - /* This routine has been called to create an automatic index as a - ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or - ** a PRIMARY KEY or UNIQUE clause following the column definitions. - ** i.e. one of: - ** - ** CREATE TABLE t(x PRIMARY KEY, y); - ** CREATE TABLE t(x, y, UNIQUE(x, y)); - ** - ** Either way, check to see if the table already has such an index. If - ** so, don't bother creating this one. This only applies to - ** automatically created indices. Users can do as they wish with - ** explicit indices. - */ - Index *pIdx; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int k; - assert( pIdx->onError!=OE_None ); - assert( pIdx->autoIndex ); - assert( pIndex->onError!=OE_None ); - - if( pIdx->nColumn!=pIndex->nColumn ) continue; - for(k=0; k<pIdx->nColumn; k++){ - if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break; - if( pIdx->keyInfo.aColl[k]!=pIndex->keyInfo.aColl[k] ) break; - } - if( k==pIdx->nColumn ){ - if( pIdx->onError!=pIndex->onError ){ - /* This constraint creates the same index as a previous - ** constraint specified somewhere in the CREATE TABLE statement. - ** However the ON CONFLICT clauses are different. If both this - ** constraint and the previous equivalent constraint have explicit - ** ON CONFLICT clauses this is an error. Otherwise, use the - ** explicitly specified behaviour for the index. - */ - if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){ - sqlite3ErrorMsg(pParse, - "conflicting ON CONFLICT clauses specified", 0); - } - if( pIdx->onError==OE_Default ){ - pIdx->onError = pIndex->onError; - } - } - goto exit_create_index; - } - } - } - - /* Link the new Index structure to its table and to the other - ** in-memory database structures. - */ - if( db->init.busy ){ - Index *p; - p = sqlite3HashInsert(&db->aDb[pIndex->iDb].idxHash, - pIndex->zName, strlen(pIndex->zName)+1, pIndex); - if( p ){ - assert( p==pIndex ); /* Malloc must have failed */ - goto exit_create_index; - } - db->flags |= SQLITE_InternChanges; - if( pTblName!=0 ){ - pIndex->tnum = db->init.newTnum; - } - } - - /* If the db->init.busy is 0 then create the index on disk. This - ** involves writing the index into the master table and filling in the - ** index with the current table contents. - ** - ** The db->init.busy is 0 when the user first enters a CREATE INDEX - ** command. db->init.busy is 1 when a database is opened and - ** CREATE INDEX statements are read out of the master table. In - ** the latter case the index already exists on disk, which is why - ** we don't want to recreate it. - ** - ** If pTblName==0 it means this index is generated as a primary key - ** or UNIQUE constraint of a CREATE TABLE statement. Since the table - ** has just been created, it contains no data and the index initialization - ** step can be skipped. - */ - else if( db->init.busy==0 ){ - int n; - Vdbe *v; - int lbl1, lbl2; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto exit_create_index; - if( pTblName!=0 ){ - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3OpenMasterTable(v, iDb); - } - sqlite3VdbeAddOp(v, OP_NewRecno, 0, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, "index", P3_STATIC); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pIndex->zName, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0); - sqlite3VdbeAddOp(v, OP_CreateIndex, iDb, 0); - if( pTblName ){ - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); - sqlite3VdbeOp3(v, OP_OpenWrite, 1, 0, - (char*)&pIndex->keyInfo, P3_KEYINFO); - } - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - if( pStart && pEnd ){ - if( onError==OE_None ){ - sqlite3VdbeChangeP3(v, -1, "CREATE INDEX ", P3_STATIC); - }else{ - sqlite3VdbeChangeP3(v, -1, "CREATE UNIQUE INDEX ", P3_STATIC); - } - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - n = Addr(pEnd->z) - Addr(pName->z) + 1; - sqlite3VdbeChangeP3(v, -1, pName->z, n); - sqlite3VdbeAddOp(v, OP_Concat, 0, 0); - } - sqlite3VdbeOp3(v, OP_MakeRecord, 5, 0, "tttit", P3_STATIC); - sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0); - if( pTblName ){ - sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenRead, 2, pTab->tnum); - /* VdbeComment((v, "%s", pTab->zName)); */ - sqlite3VdbeAddOp(v, OP_SetNumColumns, 2, pTab->nCol); - lbl2 = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Rewind, 2, lbl2); - lbl1 = sqlite3VdbeCurrentAddr(v); - sqlite3GenerateIndexKey(v, pIndex, 2); - sqlite3VdbeOp3(v, OP_IdxPut, 1, pIndex->onError!=OE_None, - "indexed columns are not unique", P3_STATIC); - sqlite3VdbeAddOp(v, OP_Next, 2, lbl1); - sqlite3VdbeResolveLabel(v, lbl2); - sqlite3VdbeAddOp(v, OP_Close, 2, 0); - sqlite3VdbeAddOp(v, OP_Close, 1, 0); - sqlite3ChangeCookie(db, v, iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, - sqlite3MPrintf("name='%q'", pIndex->zName), P3_DYNAMIC); - } - } - - /* When adding an index to the list of indices for a table, make - ** sure all indices labeled OE_Replace come after all those labeled - ** OE_Ignore. This is necessary for the correct operation of UPDATE - ** and INSERT. - */ - if( db->init.busy || pTblName==0 ){ - if( onError!=OE_Replace || pTab->pIndex==0 - || pTab->pIndex->onError==OE_Replace){ - pIndex->pNext = pTab->pIndex; - pTab->pIndex = pIndex; - }else{ - Index *pOther = pTab->pIndex; - while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){ - pOther = pOther->pNext; - } - pIndex->pNext = pOther->pNext; - pOther->pNext = pIndex; - } - pIndex = 0; - } - - /* Clean up before exiting */ -exit_create_index: - if( pIndex ){ - freeIndex(pIndex); - } - sqlite3ExprListDelete(pList); - sqlite3SrcListDelete(pTblName); - sqliteFree(zName); - return; -} - -/* -** This routine will drop an existing named index. This routine -** implements the DROP INDEX statement. -*/ -void sqlite3DropIndex(Parse *pParse, SrcList *pName){ - Index *pIndex; - Vdbe *v; - sqlite3 *db = pParse->db; - - if( pParse->nErr || sqlite3_malloc_failed ) return; - assert( pName->nSrc==1 ); - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) return; - pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); - if( pIndex==0 ){ - sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); - pParse->checkSchema = 1; - goto exit_drop_index; - } - if( pIndex->autoIndex ){ - sqlite3ErrorMsg(pParse, "index associated with UNIQUE " - "or PRIMARY KEY constraint cannot be dropped", 0); - goto exit_drop_index; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_DROP_INDEX; - Table *pTab = pIndex->pTable; - const char *zDb = db->aDb[pIndex->iDb].zName; - const char *zTab = SCHEMA_TABLE(pIndex->iDb); - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ - goto exit_drop_index; - } - if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ - goto exit_drop_index; - } - } -#endif - - /* Generate code to remove the index and from the master table */ - v = sqlite3GetVdbe(pParse); - if( v ){ - static const VdbeOpList dropIndex[] = { - { OP_Rewind, 0, ADDR(9), 0}, - { OP_String8, 0, 0, 0}, /* 1 */ - { OP_MemStore, 1, 1, 0}, - { OP_MemLoad, 1, 0, 0}, /* 3 */ - { OP_Column, 0, 1, 0}, - { OP_Eq, 0, ADDR(8), 0}, - { OP_Next, 0, ADDR(3), 0}, - { OP_Goto, 0, ADDR(9), 0}, - { OP_Delete, 0, 0, 0}, /* 8 */ - }; - int base; - - sqlite3BeginWriteOperation(pParse, 0, pIndex->iDb); - sqlite3OpenMasterTable(v, pIndex->iDb); - base = sqlite3VdbeAddOpList(v, ArraySize(dropIndex), dropIndex); - sqlite3VdbeChangeP3(v, base+1, pIndex->zName, 0); - sqlite3ChangeCookie(db, v, pIndex->iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb); - sqlite3VdbeOp3(v, OP_DropIndex, pIndex->iDb, 0, pIndex->zName, 0); - } - -exit_drop_index: - sqlite3SrcListDelete(pName); -} - -/* -** Append a new element to the given IdList. Create a new IdList if -** need be. -** -** A new IdList is returned, or NULL if malloc() fails. -*/ -IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){ - if( pList==0 ){ - pList = sqliteMalloc( sizeof(IdList) ); - if( pList==0 ) return 0; - pList->nAlloc = 0; - } - if( pList->nId>=pList->nAlloc ){ - struct IdList_item *a; - pList->nAlloc = pList->nAlloc*2 + 5; - a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]) ); - if( a==0 ){ - sqlite3IdListDelete(pList); - return 0; - } - pList->a = a; - } - memset(&pList->a[pList->nId], 0, sizeof(pList->a[0])); - pList->a[pList->nId].zName = sqlite3NameFromToken(pToken); - pList->nId++; - return pList; -} - -/* -** Append a new table name to the given SrcList. Create a new SrcList if -** need be. A new entry is created in the SrcList even if pToken is NULL. -** -** A new SrcList is returned, or NULL if malloc() fails. -** -** If pDatabase is not null, it means that the table has an optional -** database name prefix. Like this: "database.table". The pDatabase -** points to the table name and the pTable points to the database name. -** The SrcList.a[].zName field is filled with the table name which might -** come from pTable (if pDatabase is NULL) or from pDatabase. -** SrcList.a[].zDatabase is filled with the database name from pTable, -** or with NULL if no database is specified. -** -** In other words, if call like this: -** -** sqlite3SrcListAppend(A,B,0); -** -** Then B is a table name and the database name is unspecified. If called -** like this: -** -** sqlite3SrcListAppend(A,B,C); -** -** Then C is the table name and B is the database name. -*/ -SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){ - struct SrcList_item *pItem; - if( pList==0 ){ - pList = sqliteMalloc( sizeof(SrcList) ); - if( pList==0 ) return 0; - pList->nAlloc = 1; - } - if( pList->nSrc>=pList->nAlloc ){ - SrcList *pNew; - pList->nAlloc *= 2; - pNew = sqliteRealloc(pList, - sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) ); - if( pNew==0 ){ - sqlite3SrcListDelete(pList); - return 0; - } - pList = pNew; - } - pItem = &pList->a[pList->nSrc]; - memset(pItem, 0, sizeof(pList->a[0])); - if( pDatabase && pDatabase->z==0 ){ - pDatabase = 0; - } - if( pDatabase && pTable ){ - Token *pTemp = pDatabase; - pDatabase = pTable; - pTable = pTemp; - } - pItem->zName = sqlite3NameFromToken(pTable); - pItem->zDatabase = sqlite3NameFromToken(pDatabase); - pItem->iCursor = -1; - pList->nSrc++; - return pList; -} - -/* -** Assign cursors to all tables in a SrcList -*/ -void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ - int i; - for(i=0; i<pList->nSrc; i++){ - if( pList->a[i].iCursor<0 ){ - pList->a[i].iCursor = pParse->nTab++; - } - } -} - -/* -** Add an alias to the last identifier on the given identifier list. -*/ -void sqlite3SrcListAddAlias(SrcList *pList, Token *pToken){ - if( pList && pList->nSrc>0 ){ - pList->a[pList->nSrc-1].zAlias = sqlite3NameFromToken(pToken); - } -} - -/* -** Delete an IdList. -*/ -void sqlite3IdListDelete(IdList *pList){ - int i; - if( pList==0 ) return; - for(i=0; i<pList->nId; i++){ - sqliteFree(pList->a[i].zName); - } - sqliteFree(pList->a); - sqliteFree(pList); -} - -/* -** Return the index in pList of the identifier named zId. Return -1 -** if not found. -*/ -int sqlite3IdListIndex(IdList *pList, const char *zName){ - int i; - if( pList==0 ) return -1; - for(i=0; i<pList->nId; i++){ - if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; - } - return -1; -} - -/* -** Delete an entire SrcList including all its substructure. -*/ -void sqlite3SrcListDelete(SrcList *pList){ - int i; - struct SrcList_item *pItem; - if( pList==0 ) return; - for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){ - sqliteFree(pItem->zDatabase); - sqliteFree(pItem->zName); - sqliteFree(pItem->zAlias); - if( pItem->pTab && pItem->pTab->isTransient ){ - sqlite3DeleteTable(0, pItem->pTab); - } - sqlite3SelectDelete(pItem->pSelect); - sqlite3ExprDelete(pItem->pOn); - sqlite3IdListDelete(pItem->pUsing); - } - sqliteFree(pList); -} - -/* -** Begin a transaction -*/ -void sqlite3BeginTransaction(Parse *pParse, int type){ - sqlite3 *db; - Vdbe *v; - int i; - - if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; - if( pParse->nErr || sqlite3_malloc_failed ) return; - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return; - - v = sqlite3GetVdbe(pParse); - if( !v ) return; - if( type!=TK_DEFERRED ){ - for(i=0; i<db->nDb; i++){ - sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); - } - } - sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0); -} - -/* -** Commit a transaction -*/ -void sqlite3CommitTransaction(Parse *pParse){ - sqlite3 *db; - Vdbe *v; - - if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; - if( pParse->nErr || sqlite3_malloc_failed ) return; - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return; - - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 0); - } -} - -/* -** Rollback a transaction -*/ -void sqlite3RollbackTransaction(Parse *pParse){ - sqlite3 *db; - Vdbe *v; - - if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; - if( pParse->nErr || sqlite3_malloc_failed ) return; - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return; - - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 1); - } -} - -/* -** Make sure the TEMP database is open and available for use. Return -** the number of errors. Leave any error messages in the pParse structure. -*/ -static int sqlite3OpenTempDatabase(Parse *pParse){ - sqlite3 *db = pParse->db; - if( db->aDb[1].pBt==0 && !pParse->explain ){ - int rc = sqlite3BtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt); - if( rc!=SQLITE_OK ){ - sqlite3ErrorMsg(pParse, "unable to open a temporary database " - "file for storing temporary tables"); - pParse->rc = rc; - return 1; - } - if( db->flags & !db->autoCommit ){ - rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1); - if( rc!=SQLITE_OK ){ - sqlite3ErrorMsg(pParse, "unable to get a write lock on " - "the temporary database file"); - pParse->rc = rc; - return 1; - } - } - } - return 0; -} - -/* -** Generate VDBE code that will verify the schema cookie and start -** a read-transaction for all named database files. -** -** It is important that all schema cookies be verified and all -** read transactions be started before anything else happens in -** the VDBE program. But this routine can be called after much other -** code has been generated. So here is what we do: -** -** The first time this routine is called, we code an OP_Goto that -** will jump to a subroutine at the end of the program. Then we -** record every database that needs its schema verified in the -** pParse->cookieMask field. Later, after all other code has been -** generated, the subroutine that does the cookie verifications and -** starts the transactions will be coded and the OP_Goto P2 value -** will be made to point to that subroutine. The generation of the -** cookie verification subroutine code happens in sqlite3FinishCoding(). -** -** If iDb<0 then code the OP_Goto only - don't set flag to verify the -** schema on any databases. This can be used to position the OP_Goto -** early in the code, before we know if any database tables will be used. -*/ -void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - sqlite3 *db; - Vdbe *v; - int mask; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; /* This only happens if there was a prior error */ - db = pParse->db; - if( pParse->cookieGoto==0 ){ - pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0)+1; - } - if( iDb>=0 ){ - assert( iDb<db->nDb ); - assert( db->aDb[iDb].pBt!=0 || iDb==1 ); - assert( iDb<32 ); - mask = 1<<iDb; - if( (pParse->cookieMask & mask)==0 ){ - pParse->cookieMask |= mask; - pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie; - if( iDb==1 ){ - sqlite3OpenTempDatabase(pParse); - } - } - } -} - -/* -** Generate VDBE code that prepares for doing an operation that -** might change the database. -** -** This routine starts a new transaction if we are not already within -** a transaction. If we are already within a transaction, then a checkpoint -** is set if the setStatement parameter is true. A checkpoint should -** be set for operations that might fail (due to a constraint) part of -** the way through and which will need to undo some writes without having to -** rollback the whole transaction. For operations where all constraints -** can be checked before any changes are made to the database, it is never -** necessary to undo a write and the checkpoint should not be set. -** -** Only database iDb and the temp database are made writable by this call. -** If iDb==0, then the main and temp databases are made writable. If -** iDb==1 then only the temp database is made writable. If iDb>1 then the -** specified auxiliary database and the temp database are made writable. -*/ -void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - sqlite3CodeVerifySchema(pParse, iDb); - pParse->writeMask |= 1<<iDb; - if( setStatement ){ - sqlite3VdbeAddOp(v, OP_Statement, iDb, 0); - } - if( iDb!=1 && pParse->db->aDb[1].pBt!=0 ){ - sqlite3BeginWriteOperation(pParse, setStatement, 1); - } -} - -/* -** Return the transient sqlite3_value object used for encoding conversions -** during SQL compilation. -*/ -sqlite3_value *sqlite3GetTransientValue(sqlite3 *db){ - if( !db->pValue ){ - db->pValue = sqlite3ValueNew(); - } - return db->pValue; -} diff --git a/kopete/plugins/statistics/sqlite/date.c b/kopete/plugins/statistics/sqlite/date.c deleted file mode 100644 index 634e81d5..00000000 --- a/kopete/plugins/statistics/sqlite/date.c +++ /dev/null @@ -1,893 +0,0 @@ -/* -** 2003 October 31 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement date and time -** functions for SQLite. -** -** There is only one exported symbol in this file - the function -** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. -** All other code has file scope. -** -** $Id$ -** -** NOTES: -** -** SQLite processes all times and dates as Julian Day numbers. The -** dates and times are stored as the number of days since noon -** in Greenwich on November 24, 4714 B.C. according to the Gregorian -** calendar system. -** -** 1970-01-01 00:00:00 is JD 2440587.5 -** 2000-01-01 00:00:00 is JD 2451544.5 -** -** This implemention requires years to be expressed as a 4-digit number -** which means that only dates between 0000-01-01 and 9999-12-31 can -** be represented, even though julian day numbers allow a much wider -** range of dates. -** -** The Gregorian calendar system is used for all dates and times, -** even those that predate the Gregorian calendar. Historians usually -** use the Julian calendar for dates prior to 1582-10-15 and for some -** dates afterwards, depending on locale. Beware of this difference. -** -** The conversion algorithms are implemented based on descriptions -** in the following text: -** -** Jean Meeus -** Astronomical Algorithms, 2nd Edition, 1998 -** ISBM 0-943396-61-1 -** Willmann-Bell, Inc -** Richmond, Virginia (USA) -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> -#include <stdlib.h> -#include <assert.h> -#include <time.h> - -#ifndef SQLITE_OMIT_DATETIME_FUNCS - -/* -** A structure for holding a single date and time. -*/ -typedef struct DateTime DateTime; -struct DateTime { - double rJD; /* The julian day number */ - int Y, M, D; /* Year, month, and day */ - int h, m; /* Hour and minutes */ - int tz; /* Timezone offset in minutes */ - double s; /* Seconds */ - char validYMD; /* True if Y,M,D are valid */ - char validHMS; /* True if h,m,s are valid */ - char validJD; /* True if rJD is valid */ - char validTZ; /* True if tz is valid */ -}; - - -/* -** Convert zDate into one or more integers. Additional arguments -** come in groups of 5 as follows: -** -** N number of digits in the integer -** min minimum allowed value of the integer -** max maximum allowed value of the integer -** nextC first character after the integer -** pVal where to write the integers value. -** -** Conversions continue until one with nextC==0 is encountered. -** The function returns the number of successful conversions. -*/ -static int getDigits(const char *zDate, ...){ - va_list ap; - int val; - int N; - int min; - int max; - int nextC; - int *pVal; - int cnt = 0; - va_start(ap, zDate); - do{ - N = va_arg(ap, int); - min = va_arg(ap, int); - max = va_arg(ap, int); - nextC = va_arg(ap, int); - pVal = va_arg(ap, int*); - val = 0; - while( N-- ){ - if( !isdigit(*(u8*)zDate) ){ - return cnt; - } - val = val*10 + *zDate - '0'; - zDate++; - } - if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){ - return cnt; - } - *pVal = val; - zDate++; - cnt++; - }while( nextC ); - return cnt; -} - -/* -** Read text from z[] and convert into a floating point number. Return -** the number of digits converted. -*/ -static int getValue(const char *z, double *pR){ - const char *zEnd; - *pR = sqlite3AtoF(z, &zEnd); - return zEnd - z; -} - -/* -** Parse a timezone extension on the end of a date-time. -** The extension is of the form: -** -** (+/-)HH:MM -** -** If the parse is successful, write the number of minutes -** of change in *pnMin and return 0. If a parser error occurs, -** return 0. -** -** A missing specifier is not considered an error. -*/ -static int parseTimezone(const char *zDate, DateTime *p){ - int sgn = 0; - int nHr, nMn; - while( isspace(*(u8*)zDate) ){ zDate++; } - p->tz = 0; - if( *zDate=='-' ){ - sgn = -1; - }else if( *zDate=='+' ){ - sgn = +1; - }else{ - return *zDate!=0; - } - zDate++; - if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){ - return 1; - } - zDate += 5; - p->tz = sgn*(nMn + nHr*60); - while( isspace(*(u8*)zDate) ){ zDate++; } - return *zDate!=0; -} - -/* -** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. -** The HH, MM, and SS must each be exactly 2 digits. The -** fractional seconds FFFF can be one or more digits. -** -** Return 1 if there is a parsing error and 0 on success. -*/ -static int parseHhMmSs(const char *zDate, DateTime *p){ - int h, m, s; - double ms = 0.0; - if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){ - return 1; - } - zDate += 5; - if( *zDate==':' ){ - zDate++; - if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){ - return 1; - } - zDate += 2; - if( *zDate=='.' && isdigit((u8)zDate[1]) ){ - double rScale = 1.0; - zDate++; - while( isdigit(*(u8*)zDate) ){ - ms = ms*10.0 + *zDate - '0'; - rScale *= 10.0; - zDate++; - } - ms /= rScale; - } - }else{ - s = 0; - } - p->validJD = 0; - p->validHMS = 1; - p->h = h; - p->m = m; - p->s = s + ms; - if( parseTimezone(zDate, p) ) return 1; - p->validTZ = p->tz!=0; - return 0; -} - -/* -** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume -** that the YYYY-MM-DD is according to the Gregorian calendar. -** -** Reference: Meeus page 61 -*/ -static void computeJD(DateTime *p){ - int Y, M, D, A, B, X1, X2; - - if( p->validJD ) return; - if( p->validYMD ){ - Y = p->Y; - M = p->M; - D = p->D; - }else{ - Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ - M = 1; - D = 1; - } - if( M<=2 ){ - Y--; - M += 12; - } - A = Y/100; - B = 2 - A + (A/4); - X1 = 365.25*(Y+4716); - X2 = 30.6001*(M+1); - p->rJD = X1 + X2 + D + B - 1524.5; - p->validJD = 1; - p->validYMD = 0; - if( p->validHMS ){ - p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0; - if( p->validTZ ){ - p->rJD += p->tz*60/86400.0; - p->validHMS = 0; - p->validTZ = 0; - } - } -} - -/* -** Parse dates of the form -** -** YYYY-MM-DD HH:MM:SS.FFF -** YYYY-MM-DD HH:MM:SS -** YYYY-MM-DD HH:MM -** YYYY-MM-DD -** -** Write the result into the DateTime structure and return 0 -** on success and 1 if the input string is not a well-formed -** date. -*/ -static int parseYyyyMmDd(const char *zDate, DateTime *p){ - int Y, M, D, neg; - - if( zDate[0]=='-' ){ - zDate++; - neg = 1; - }else{ - neg = 0; - } - if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){ - return 1; - } - zDate += 10; - while( isspace(*(u8*)zDate) ){ zDate++; } - if( parseHhMmSs(zDate, p)==0 ){ - /* We got the time */ - }else if( *zDate==0 ){ - p->validHMS = 0; - }else{ - return 1; - } - p->validJD = 0; - p->validYMD = 1; - p->Y = neg ? -Y : Y; - p->M = M; - p->D = D; - if( p->validTZ ){ - computeJD(p); - } - return 0; -} - -/* -** Attempt to parse the given string into a Julian Day Number. Return -** the number of errors. -** -** The following are acceptable forms for the input string: -** -** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM -** DDDD.DD -** now -** -** In the first form, the +/-HH:MM is always optional. The fractional -** seconds extension (the ".FFF") is optional. The seconds portion -** (":SS.FFF") is option. The year and date can be omitted as long -** as there is a time string. The time string can be omitted as long -** as there is a year and date. -*/ -static int parseDateOrTime(const char *zDate, DateTime *p){ - memset(p, 0, sizeof(*p)); - if( parseYyyyMmDd(zDate,p)==0 ){ - return 0; - }else if( parseHhMmSs(zDate, p)==0 ){ - return 0; - }else if( sqlite3StrICmp(zDate,"now")==0){ - double r; - if( sqlite3OsCurrentTime(&r)==0 ){ - p->rJD = r; - p->validJD = 1; - return 0; - } - return 1; - }else if( sqlite3IsNumber(zDate, 0, SQLITE_UTF8) ){ - p->rJD = sqlite3AtoF(zDate, 0); - p->validJD = 1; - return 0; - } - return 1; -} - -/* -** Compute the Year, Month, and Day from the julian day number. -*/ -static void computeYMD(DateTime *p){ - int Z, A, B, C, D, E, X1; - if( p->validYMD ) return; - if( !p->validJD ){ - p->Y = 2000; - p->M = 1; - p->D = 1; - }else{ - Z = p->rJD + 0.5; - A = (Z - 1867216.25)/36524.25; - A = Z + 1 + A - (A/4); - B = A + 1524; - C = (B - 122.1)/365.25; - D = 365.25*C; - E = (B-D)/30.6001; - X1 = 30.6001*E; - p->D = B - D - X1; - p->M = E<14 ? E-1 : E-13; - p->Y = p->M>2 ? C - 4716 : C - 4715; - } - p->validYMD = 1; -} - -/* -** Compute the Hour, Minute, and Seconds from the julian day number. -*/ -static void computeHMS(DateTime *p){ - int Z, s; - if( p->validHMS ) return; - Z = p->rJD + 0.5; - s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5; - p->s = 0.001*s; - s = p->s; - p->s -= s; - p->h = s/3600; - s -= p->h*3600; - p->m = s/60; - p->s += s - p->m*60; - p->validHMS = 1; -} - -/* -** Compute both YMD and HMS -*/ -static void computeYMD_HMS(DateTime *p){ - computeYMD(p); - computeHMS(p); -} - -/* -** Clear the YMD and HMS and the TZ -*/ -static void clearYMD_HMS_TZ(DateTime *p){ - p->validYMD = 0; - p->validHMS = 0; - p->validTZ = 0; -} - -/* -** Compute the difference (in days) between localtime and UTC (a.k.a. GMT) -** for the time value p where p is in UTC. -*/ -static double localtimeOffset(DateTime *p){ - DateTime x, y; - time_t t; - struct tm *pTm; - x = *p; - computeYMD_HMS(&x); - if( x.Y<1971 || x.Y>=2038 ){ - x.Y = 2000; - x.M = 1; - x.D = 1; - x.h = 0; - x.m = 0; - x.s = 0.0; - } else { - int s = x.s + 0.5; - x.s = s; - } - x.tz = 0; - x.validJD = 0; - computeJD(&x); - t = (x.rJD-2440587.5)*86400.0 + 0.5; - sqlite3OsEnterMutex(); - pTm = localtime(&t); - y.Y = pTm->tm_year + 1900; - y.M = pTm->tm_mon + 1; - y.D = pTm->tm_mday; - y.h = pTm->tm_hour; - y.m = pTm->tm_min; - y.s = pTm->tm_sec; - sqlite3OsLeaveMutex(); - y.validYMD = 1; - y.validHMS = 1; - y.validJD = 0; - y.validTZ = 0; - computeJD(&y); - return y.rJD - x.rJD; -} - -/* -** Process a modifier to a date-time stamp. The modifiers are -** as follows: -** -** NNN days -** NNN hours -** NNN minutes -** NNN.NNNN seconds -** NNN months -** NNN years -** start of month -** start of year -** start of week -** start of day -** weekday N -** unixepoch -** localtime -** utc -** -** Return 0 on success and 1 if there is any kind of error. -*/ -static int parseModifier(const char *zMod, DateTime *p){ - int rc = 1; - int n; - double r; - char *z, zBuf[30]; - z = zBuf; - for(n=0; n<sizeof(zBuf)-1 && zMod[n]; n++){ - z[n] = tolower(zMod[n]); - } - z[n] = 0; - switch( z[0] ){ - case 'l': { - /* localtime - ** - ** Assuming the current time value is UTC (a.k.a. GMT), shift it to - ** show local time. - */ - if( strcmp(z, "localtime")==0 ){ - computeJD(p); - p->rJD += localtimeOffset(p); - clearYMD_HMS_TZ(p); - rc = 0; - } - break; - } - case 'u': { - /* - ** unixepoch - ** - ** Treat the current value of p->rJD as the number of - ** seconds since 1970. Convert to a real julian day number. - */ - if( strcmp(z, "unixepoch")==0 && p->validJD ){ - p->rJD = p->rJD/86400.0 + 2440587.5; - clearYMD_HMS_TZ(p); - rc = 0; - }else if( strcmp(z, "utc")==0 ){ - double c1; - computeJD(p); - c1 = localtimeOffset(p); - p->rJD -= c1; - clearYMD_HMS_TZ(p); - p->rJD += c1 - localtimeOffset(p); - rc = 0; - } - break; - } - case 'w': { - /* - ** weekday N - ** - ** Move the date to the same time on the next occurrence of - ** weekday N where 0==Sunday, 1==Monday, and so forth. If the - ** date is already on the appropriate weekday, this is a no-op. - */ - if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0 - && (n=r)==r && n>=0 && r<7 ){ - int Z; - computeYMD_HMS(p); - p->validTZ = 0; - p->validJD = 0; - computeJD(p); - Z = p->rJD + 1.5; - Z %= 7; - if( Z>n ) Z -= 7; - p->rJD += n - Z; - clearYMD_HMS_TZ(p); - rc = 0; - } - break; - } - case 's': { - /* - ** start of TTTTT - ** - ** Move the date backwards to the beginning of the current day, - ** or month or year. - */ - if( strncmp(z, "start of ", 9)!=0 ) break; - z += 9; - computeYMD(p); - p->validHMS = 1; - p->h = p->m = 0; - p->s = 0.0; - p->validTZ = 0; - p->validJD = 0; - if( strcmp(z,"month")==0 ){ - p->D = 1; - rc = 0; - }else if( strcmp(z,"year")==0 ){ - computeYMD(p); - p->M = 1; - p->D = 1; - rc = 0; - }else if( strcmp(z,"day")==0 ){ - rc = 0; - } - break; - } - case '+': - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - n = getValue(z, &r); - if( n<=0 ) break; - if( z[n]==':' ){ - /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the - ** specified number of hours, minutes, seconds, and fractional seconds - ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be - ** omitted. - */ - const char *z2 = z; - DateTime tx; - int day; - if( !isdigit(*(u8*)z2) ) z2++; - memset(&tx, 0, sizeof(tx)); - if( parseHhMmSs(z2, &tx) ) break; - computeJD(&tx); - tx.rJD -= 0.5; - day = (int)tx.rJD; - tx.rJD -= day; - if( z[0]=='-' ) tx.rJD = -tx.rJD; - computeJD(p); - clearYMD_HMS_TZ(p); - p->rJD += tx.rJD; - rc = 0; - break; - } - z += n; - while( isspace(*(u8*)z) ) z++; - n = strlen(z); - if( n>10 || n<3 ) break; - if( z[n-1]=='s' ){ z[n-1] = 0; n--; } - computeJD(p); - rc = 0; - if( n==3 && strcmp(z,"day")==0 ){ - p->rJD += r; - }else if( n==4 && strcmp(z,"hour")==0 ){ - p->rJD += r/24.0; - }else if( n==6 && strcmp(z,"minute")==0 ){ - p->rJD += r/(24.0*60.0); - }else if( n==6 && strcmp(z,"second")==0 ){ - p->rJD += r/(24.0*60.0*60.0); - }else if( n==5 && strcmp(z,"month")==0 ){ - int x, y; - computeYMD_HMS(p); - p->M += r; - x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; - p->Y += x; - p->M -= x*12; - p->validJD = 0; - computeJD(p); - y = r; - if( y!=r ){ - p->rJD += (r - y)*30.0; - } - }else if( n==4 && strcmp(z,"year")==0 ){ - computeYMD_HMS(p); - p->Y += r; - p->validJD = 0; - computeJD(p); - }else{ - rc = 1; - } - clearYMD_HMS_TZ(p); - break; - } - default: { - break; - } - } - return rc; -} - -/* -** Process time function arguments. argv[0] is a date-time stamp. -** argv[1] and following are modifiers. Parse them all and write -** the resulting time into the DateTime structure p. Return 0 -** on success and 1 if there are any errors. -*/ -static int isDate(int argc, sqlite3_value **argv, DateTime *p){ - int i; - if( argc==0 ) return 1; - if( SQLITE_NULL==sqlite3_value_type(argv[0]) || - parseDateOrTime(sqlite3_value_text(argv[0]), p) ) return 1; - for(i=1; i<argc; i++){ - if( SQLITE_NULL==sqlite3_value_type(argv[i]) || - parseModifier(sqlite3_value_text(argv[i]), p) ) return 1; - } - return 0; -} - - -/* -** The following routines implement the various date and time functions -** of SQLite. -*/ - -/* -** julianday( TIMESTRING, MOD, MOD, ...) -** -** Return the julian day number of the date specified in the arguments -*/ -static void juliandayFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - DateTime x; - if( isDate(argc, argv, &x)==0 ){ - computeJD(&x); - sqlite3_result_double(context, x.rJD); - } -} - -/* -** datetime( TIMESTRING, MOD, MOD, ...) -** -** Return YYYY-MM-DD HH:MM:SS -*/ -static void datetimeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - DateTime x; - if( isDate(argc, argv, &x)==0 ){ - char zBuf[100]; - computeYMD_HMS(&x); - sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d",x.Y, x.M, x.D, x.h, x.m, - (int)(x.s)); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - } -} - -/* -** time( TIMESTRING, MOD, MOD, ...) -** -** Return HH:MM:SS -*/ -static void timeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - DateTime x; - if( isDate(argc, argv, &x)==0 ){ - char zBuf[100]; - computeHMS(&x); - sprintf(zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - } -} - -/* -** date( TIMESTRING, MOD, MOD, ...) -** -** Return YYYY-MM-DD -*/ -static void dateFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - DateTime x; - if( isDate(argc, argv, &x)==0 ){ - char zBuf[100]; - computeYMD(&x); - sprintf(zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - } -} - -/* -** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) -** -** Return a string described by FORMAT. Conversions as follows: -** -** %d day of month -** %f ** fractional seconds SS.SSS -** %H hour 00-24 -** %j day of year 000-366 -** %J ** Julian day number -** %m month 01-12 -** %M minute 00-59 -** %s seconds since 1970-01-01 -** %S seconds 00-59 -** %w day of week 0-6 sunday==0 -** %W week of year 00-53 -** %Y year 0000-9999 -** %% % -*/ -static void strftimeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - DateTime x; - int n, i, j; - char *z; - const char *zFmt = sqlite3_value_text(argv[0]); - char zBuf[100]; - if( zFmt==0 || isDate(argc-1, argv+1, &x) ) return; - for(i=0, n=1; zFmt[i]; i++, n++){ - if( zFmt[i]=='%' ){ - switch( zFmt[i+1] ){ - case 'd': - case 'H': - case 'm': - case 'M': - case 'S': - case 'W': - n++; - /* fall thru */ - case 'w': - case '%': - break; - case 'f': - n += 8; - break; - case 'j': - n += 3; - break; - case 'Y': - n += 8; - break; - case 's': - case 'J': - n += 50; - break; - default: - return; /* ERROR. return a NULL */ - } - i++; - } - } - if( n<sizeof(zBuf) ){ - z = zBuf; - }else{ - z = sqliteMalloc( n ); - if( z==0 ) return; - } - computeJD(&x); - computeYMD_HMS(&x); - for(i=j=0; zFmt[i]; i++){ - if( zFmt[i]!='%' ){ - z[j++] = zFmt[i]; - }else{ - i++; - switch( zFmt[i] ){ - case 'd': sprintf(&z[j],"%02d",x.D); j+=2; break; - case 'f': { - int s = x.s; - int ms = (x.s - s)*1000.0; - sprintf(&z[j],"%02d.%03d",s,ms); - j += strlen(&z[j]); - break; - } - case 'H': sprintf(&z[j],"%02d",x.h); j+=2; break; - case 'W': /* Fall thru */ - case 'j': { - int n; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - n = x.rJD - y.rJD; - if( zFmt[i]=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = ((int)(x.rJD+0.5)) % 7; - sprintf(&z[j],"%02d",(n+7-wd)/7); - j += 2; - }else{ - sprintf(&z[j],"%03d",n+1); - j += 3; - } - break; - } - case 'J': sprintf(&z[j],"%.16g",x.rJD); j+=strlen(&z[j]); break; - case 'm': sprintf(&z[j],"%02d",x.M); j+=2; break; - case 'M': sprintf(&z[j],"%02d",x.m); j+=2; break; - case 's': { - sprintf(&z[j],"%d",(int)((x.rJD-2440587.5)*86400.0 + 0.5)); - j += strlen(&z[j]); - break; - } - case 'S': sprintf(&z[j],"%02d",(int)(x.s+0.5)); j+=2; break; - case 'w': z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break; - case 'Y': sprintf(&z[j],"%04d",x.Y); j+=strlen(&z[j]); break; - case '%': z[j++] = '%'; break; - } - } - } - z[j] = 0; - sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); - if( z!=zBuf ){ - sqliteFree(z); - } -} - - -#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */ - -/* -** This function registered all of the above C functions as SQL -** functions. This should be the only routine in this file with -** external linkage. -*/ -void sqlite3RegisterDateTimeFunctions(sqlite3 *db){ -#ifndef SQLITE_OMIT_DATETIME_FUNCS - static const struct { - char *zName; - int nArg; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } aFuncs[] = { - { "julianday", -1, juliandayFunc }, - { "date", -1, dateFunc }, - { "time", -1, timeFunc }, - { "datetime", -1, datetimeFunc }, - { "strftime", -1, strftimeFunc }, - }; - int i; - - for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ - sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, - SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0); - } -#endif -} diff --git a/kopete/plugins/statistics/sqlite/delete.c b/kopete/plugins/statistics/sqlite/delete.c deleted file mode 100644 index 866da61d..00000000 --- a/kopete/plugins/statistics/sqlite/delete.c +++ /dev/null @@ -1,419 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle DELETE FROM statements. -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** Look up every table that is named in pSrc. If any table is not found, -** add an error message to pParse->zErrMsg and return NULL. If all tables -** are found, return a pointer to the last table. -*/ -Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ - Table *pTab = 0; - int i; - struct SrcList_item *pItem; - for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){ - pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase); - pItem->pTab = pTab; - } - return pTab; -} - -/* -** Check to make sure the given table is writable. If it is not -** writable, generate an error message and return 1. If it is -** writable return 0; -*/ -int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - if( pTab->readOnly ){ - sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); - return 1; - } - if( !viewOk && pTab->pSelect ){ - sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); - return 1; - } - return 0; -} - -/* -** Generate code that will open a table for reading. -*/ -void sqlite3OpenTableForReading( - Vdbe *v, /* Generate code into this VDBE */ - int iCur, /* The cursor number of the table */ - Table *pTab /* The table to be opened */ -){ - sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); - VdbeComment((v, "# %s", pTab->zName)); - sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); -} - - -/* -** Process a DELETE FROM statement. -*/ -void sqlite3DeleteFrom( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* The table from which we should delete things */ - Expr *pWhere /* The WHERE clause. May be null */ -){ - Vdbe *v; /* The virtual database engine */ - Table *pTab; /* The table from which records will be deleted */ - const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ - int i; /* Loop counter */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ - Index *pIdx; /* For looping over indices of the table */ - int iCur; /* VDBE Cursor number for pTab */ - sqlite3 *db; /* Main database structure */ - int isView; /* True if attempting to delete from a view */ - AuthContext sContext; /* Authorization context */ - - int row_triggers_exist = 0; /* True if any triggers exist */ - int before_triggers; /* True if there are BEFORE triggers */ - int after_triggers; /* True if there are AFTER triggers */ - int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ - - sContext.pParse = 0; - if( pParse->nErr || sqlite3_malloc_failed ){ - pTabList = 0; - goto delete_from_cleanup; - } - db = pParse->db; - assert( pTabList->nSrc==1 ); - - /* Locate the table which we want to delete. This table has to be - ** put in an SrcList structure because some of the subroutines we - ** will be calling are designed to work with multiple tables and expect - ** an SrcList* parameter instead of just a Table* parameter. - */ - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ) goto delete_from_cleanup; - before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, - TK_DELETE, TK_BEFORE, TK_ROW, 0); - after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, - TK_DELETE, TK_AFTER, TK_ROW, 0); - row_triggers_exist = before_triggers || after_triggers; - isView = pTab->pSelect!=0; - if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){ - goto delete_from_cleanup; - } - assert( pTab->iDb<db->nDb ); - zDb = db->aDb[pTab->iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ - goto delete_from_cleanup; - } - - /* If pTab is really a view, make sure it has been initialized. - */ - if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - - /* Allocate a cursor used to store the old.* data for a trigger. - */ - if( row_triggers_exist ){ - oldIdx = pParse->nTab++; - } - - /* Resolve the column names in all the expressions. - */ - assert( pTabList->nSrc==1 ); - iCur = pTabList->a[0].iCursor = pParse->nTab++; - if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){ - goto delete_from_cleanup; - } - - /* Start the view context - */ - if( isView ){ - sqlite3AuthContextPush(pParse, &sContext, pTab->zName); - } - - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ){ - goto delete_from_cleanup; - } - sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, row_triggers_exist, pTab->iDb); - - /* If we are trying to delete from a view, construct that view into - ** a temporary table. - */ - if( isView ){ - Select *pView = sqlite3SelectDup(pTab->pSelect); - sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0); - sqlite3SelectDelete(pView); - } - - /* Initialize the counter of the number of rows deleted, if - ** we are counting rows. - */ - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - } - - /* Special case: A DELETE without a WHERE clause deletes everything. - ** It is easier just to erase the whole table. Note, however, that - ** this means that the row change count will be incorrect. - */ - if( pWhere==0 && !row_triggers_exist ){ - if( db->flags & SQLITE_CountRows ){ - /* If counting rows deleted, just count the total number of - ** entries in the table. */ - int endOfLoop = sqlite3VdbeMakeLabel(v); - int addr; - if( !isView ){ - sqlite3OpenTableForReading(v, iCur, pTab); - } - sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2); - addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); - sqlite3VdbeAddOp(v, OP_Next, iCur, addr); - sqlite3VdbeResolveLabel(v, endOfLoop); - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - if( !isView ){ - sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb); - } - } - } - - /* The usual case: There is a WHERE clause so we have to scan through - ** the table and pick which records to delete. - */ - else{ - /* Ensure all required collation sequences are available. */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){ - goto delete_from_cleanup; - } - } - - /* Begin the database scan - */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); - if( pWInfo==0 ) goto delete_from_cleanup; - - /* Remember the key of every item to be deleted. - */ - sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); - } - - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - - /* Open the pseudo-table used to store OLD if there are triggers. - */ - if( row_triggers_exist ){ - sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol); - } - - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. - */ - sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0); - end = sqlite3VdbeMakeLabel(v); - - /* This is the beginning of the delete loop when there are - ** row triggers. - */ - if( row_triggers_exist ){ - addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - if( !isView ){ - sqlite3OpenTableForReading(v, iCur, pTab); - } - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - sqlite3VdbeAddOp(v, OP_RowData, iCur, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0); - if( !isView ){ - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - - sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, - oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, - addr); - } - - if( !isView ){ - /* Open cursors for the table we are deleting from and all its - ** indices. If there are row triggers, this happens inside the - ** OP_ListRead loop because the cursor have to all be closed - ** before the trigger fires. If there are no row triggers, the - ** cursors are opened only once on the outside the loop. - */ - sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); - - /* This is the beginning of the delete loop when there are no - ** row triggers */ - if( !row_triggers_exist ){ - addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end); - } - - /* Delete the row */ - sqlite3GenerateRowDelete(db, v, pTab, iCur, 1); - } - - /* If there are row triggers, close all cursors then invoke - ** the AFTER triggers - */ - if( row_triggers_exist ){ - if( !isView ){ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); - } - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, - oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, - addr); - } - - /* End of the delete loop */ - sqlite3VdbeAddOp(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end); - sqlite3VdbeAddOp(v, OP_ListReset, 0, 0); - - /* Close the cursors after the loop if there are no row triggers */ - if( !row_triggers_exist ){ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); - } - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - } - - /* - ** Return the number of rows that were deleted. - */ - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "rows deleted", P3_STATIC); - } - -delete_from_cleanup: - sqlite3AuthContextPop(&sContext); - sqlite3SrcListDelete(pTabList); - sqlite3ExprDelete(pWhere); - return; -} - -/* -** This routine generates VDBE code that causes a single row of a -** single table to be deleted. -** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: -** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "base". -** -** 2. Read/write cursors for all indices of pTab must be open as -** cursor number base+i for the i-th index. -** -** 3. The record number of the row to be deleted must be on the top -** of the stack. -** -** This routine pops the top of the stack to remove the record number -** and then generates code to remove both the table record and all index -** entries that point to that record. -*/ -void sqlite3GenerateRowDelete( - sqlite3 *db, /* The database containing the index */ - Vdbe *v, /* Generate code into this VDBE */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int count /* Increment the row change counter */ -){ - int addr; - addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0); - sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, 0); - sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); - sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v)); -} - -/* -** This routine generates VDBE code that causes the deletion of all -** index entries associated with a single row of a single table. -** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: -** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "iCur". -** -** 2. Read/write cursors for all indices of pTab must be open as -** cursor number iCur+i for the i-th index. -** -** 3. The "iCur" cursor must be pointing to the row that is to be -** deleted. -*/ -void sqlite3GenerateRowIndexDelete( - sqlite3 *db, /* The database containing the index */ - Vdbe *v, /* Generate code into this VDBE */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */ -){ - int i; - Index *pIdx; - - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue; - sqlite3GenerateIndexKey(v, pIdx, iCur); - sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0); - } -} - -/* -** Generate code that will assemble an index key and put it on the top -** of the tack. The key with be for index pIdx which is an index on pTab. -** iCur is the index of a cursor open on the pTab table and pointing to -** the entry that needs indexing. -*/ -void sqlite3GenerateIndexKey( - Vdbe *v, /* Generate code into this VDBE */ - Index *pIdx, /* The index for which to generate a key */ - int iCur /* Cursor number for the pIdx->pTable table */ -){ - int j; - Table *pTab = pIdx->pTable; - - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - for(j=0; j<pIdx->nColumn; j++){ - int idx = pIdx->aiColumn[j]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp(v, OP_Dup, j, 0); - }else{ - sqlite3VdbeAddOp(v, OP_Column, iCur, idx); - } - } - sqlite3VdbeAddOp(v, OP_MakeRecord, pIdx->nColumn, (1<<24)); - sqlite3IndexAffinityStr(v, pIdx); -} diff --git a/kopete/plugins/statistics/sqlite/encode.c b/kopete/plugins/statistics/sqlite/encode.c deleted file mode 100644 index b10c96b3..00000000 --- a/kopete/plugins/statistics/sqlite/encode.c +++ /dev/null @@ -1,257 +0,0 @@ -/* -** 2002 April 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains helper routines used to translate binary data into -** a null-terminated string (suitable for use in SQLite) and back again. -** These are convenience routines for use by people who want to store binary -** data in an SQLite database. The code in this file is not used by any other -** part of the SQLite library. -** -** $Id$ -*/ -#include <string.h> -#include <assert.h> - -/* -** How This Encoder Works -** -** The output is allowed to contain any character except 0x27 (') and -** 0x00. This is accomplished by using an escape character to encode -** 0x27 and 0x00 as a two-byte sequence. The escape character is always -** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The -** 0x27 character is encoded as the two byte sequence 0x01 0x28. Finally, -** the escape character itself is encoded as the two-character sequence -** 0x01 0x02. -** -** To summarize, the encoder works by using an escape sequences as follows: -** -** 0x00 -> 0x01 0x01 -** 0x01 -> 0x01 0x02 -** 0x27 -> 0x01 0x28 -** -** If that were all the encoder did, it would work, but in certain cases -** it could double the size of the encoded string. For example, to -** encode a string of 100 0x27 characters would require 100 instances of -** the 0x01 0x03 escape sequence resulting in a 200-character output. -** We would prefer to keep the size of the encoded string smaller than -** this. -** -** To minimize the encoding size, we first add a fixed offset value to each -** byte in the sequence. The addition is modulo 256. (That is to say, if -** the sum of the original character value and the offset exceeds 256, then -** the higher order bits are truncated.) The offset is chosen to minimize -** the number of characters in the string that need to be escaped. For -** example, in the case above where the string was composed of 100 0x27 -** characters, the offset might be 0x01. Each of the 0x27 characters would -** then be converted into an 0x28 character which would not need to be -** escaped at all and so the 100 character input string would be converted -** into just 100 characters of output. Actually 101 characters of output - -** we have to record the offset used as the first byte in the sequence so -** that the string can be decoded. Since the offset value is stored as -** part of the output string and the output string is not allowed to contain -** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. -** -** Here, then, are the encoding steps: -** -** (1) Choose an offset value and make it the first character of -** output. -** -** (2) Copy each input character into the output buffer, one by -** one, adding the offset value as you copy. -** -** (3) If the value of an input character plus offset is 0x00, replace -** that one character by the two-character sequence 0x01 0x01. -** If the sum is 0x01, replace it with 0x01 0x02. If the sum -** is 0x27, replace it with 0x01 0x03. -** -** (4) Put a 0x00 terminator at the end of the output. -** -** Decoding is obvious: -** -** (5) Copy encoded characters except the first into the decode -** buffer. Set the first encoded character aside for use as -** the offset in step 7 below. -** -** (6) Convert each 0x01 0x01 sequence into a single character 0x00. -** Convert 0x01 0x02 into 0x01. Convert 0x01 0x28 into 0x27. -** -** (7) Subtract the offset value that was the first character of -** the encoded buffer from all characters in the output buffer. -** -** The only tricky part is step (1) - how to compute an offset value to -** minimize the size of the output buffer. This is accomplished by testing -** all offset values and picking the one that results in the fewest number -** of escapes. To do that, we first scan the entire input and count the -** number of occurances of each character value in the input. Suppose -** the number of 0x00 characters is N(0), the number of occurances of 0x01 -** is N(1), and so forth up to the number of occurances of 0xff is N(255). -** An offset of 0 is not allowed so we don't have to test it. The number -** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number -** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. -** In this way we find the offset that gives the minimum number of escapes, -** and thus minimizes the length of the output string. -*/ - -/* -** Encode a binary buffer "in" of size n bytes so that it contains -** no instances of characters '\'' or '\000'. The output is -** null-terminated and can be used as a string value in an INSERT -** or UPDATE statement. Use sqlite_decode_binary() to convert the -** string back into its original binary. -** -** The result is written into a preallocated output buffer "out". -** "out" must be able to hold at least 2 +(257*n)/254 bytes. -** In other words, the output will be expanded by as much as 3 -** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. -** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) -** -** The return value is the number of characters in the encoded -** string, excluding the "\000" terminator. -** -** If out==NULL then no output is generated but the routine still returns -** the number of characters that would have been generated if out had -** not been NULL. -*/ -int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){ - int i, j, e, m; - unsigned char x; - int cnt[256]; - if( n<=0 ){ - if( out ){ - out[0] = 'x'; - out[1] = 0; - } - return 1; - } - memset(cnt, 0, sizeof(cnt)); - for(i=n-1; i>=0; i--){ cnt[in[i]]++; } - m = n; - for(i=1; i<256; i++){ - int sum; - if( i=='\'' ) continue; - sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; - if( sum<m ){ - m = sum; - e = i; - if( m==0 ) break; - } - } - if( out==0 ){ - return n+m+1; - } - out[0] = e; - j = 1; - for(i=0; i<n; i++){ - x = in[i] - e; - if( x==0 || x==1 || x=='\''){ - out[j++] = 1; - x++; - } - out[j++] = x; - } - out[j] = 0; - assert( j==n+m+1 ); - return j; -} - -/* -** Decode the string "in" into binary data and write it into "out". -** This routine reverses the encoding created by sqlite_encode_binary(). -** The output will always be a few bytes less than the input. The number -** of bytes of output is returned. If the input is not a well-formed -** encoding, -1 is returned. -** -** The "in" and "out" parameters may point to the same buffer in order -** to decode a string in place. -*/ -int sqlite_decode_binary(const unsigned char *in, unsigned char *out){ - int i, e; - unsigned char c; - e = *(in++); - i = 0; - while( (c = *(in++))!=0 ){ - if( c==1 ){ - c = *(in++) - 1; - } - out[i++] = c + e; - } - return i; -} - -#ifdef ENCODER_TEST -#include <stdio.h> -/* -** The subroutines above are not tested by the usual test suite. To test -** these routines, compile just this one file with a -DENCODER_TEST=1 option -** and run the result. -*/ -int main(int argc, char **argv){ - int i, j, n, m, nOut, nByteIn, nByteOut; - unsigned char in[30000]; - unsigned char out[33000]; - - nByteIn = nByteOut = 0; - for(i=0; i<sizeof(in); i++){ - printf("Test %d: ", i+1); - n = rand() % (i+1); - if( i%100==0 ){ - int k; - for(j=k=0; j<n; j++){ - /* if( k==0 || k=='\'' ) k++; */ - in[j] = k; - k = (k+1)&0xff; - } - }else{ - for(j=0; j<n; j++) in[j] = rand() & 0xff; - } - nByteIn += n; - nOut = sqlite_encode_binary(in, n, out); - nByteOut += nOut; - if( nOut!=strlen(out) ){ - printf(" ERROR return value is %d instead of %d\n", nOut, strlen(out)); - exit(1); - } - if( nOut!=sqlite_encode_binary(in, n, 0) ){ - printf(" ERROR actual output size disagrees with predicted size\n"); - exit(1); - } - m = (256*n + 1262)/253; - printf("size %d->%d (max %d)", n, strlen(out)+1, m); - if( strlen(out)+1>m ){ - printf(" ERROR output too big\n"); - exit(1); - } - for(j=0; out[j]; j++){ - if( out[j]=='\'' ){ - printf(" ERROR contains (')\n"); - exit(1); - } - } - j = sqlite_decode_binary(out, out); - if( j!=n ){ - printf(" ERROR decode size %d\n", j); - exit(1); - } - if( memcmp(in, out, n)!=0 ){ - printf(" ERROR decode mismatch\n"); - exit(1); - } - printf(" OK\n"); - } - fprintf(stderr,"Finished. Total encoding: %d->%d bytes\n", - nByteIn, nByteOut); - fprintf(stderr,"Avg size increase: %.3f%%\n", - (nByteOut-nByteIn)*100.0/(double)nByteIn); -} -#endif /* ENCODER_TEST */ - - - diff --git a/kopete/plugins/statistics/sqlite/expr.c b/kopete/plugins/statistics/sqlite/expr.c deleted file mode 100644 index 2da3645b..00000000 --- a/kopete/plugins/statistics/sqlite/expr.c +++ /dev/null @@ -1,1927 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains routines used for analyzing expressions and -** for generating VDBE code that evaluates expressions in SQLite. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include <ctype.h> - -/* -** Return the 'affinity' of the expression pExpr if any. -** -** If pExpr is a column, a reference to a column via an 'AS' alias, -** or a sub-select with a column as the return value, then the -** affinity of that column is returned. Otherwise, 0x00 is returned, -** indicating no affinity for the expression. -** -** i.e. the WHERE clause expresssions in the following statements all -** have an affinity: -** -** CREATE TABLE t1(a); -** SELECT * FROM t1 WHERE a; -** SELECT a AS b FROM t1 WHERE b; -** SELECT * FROM t1 WHERE (select a from t1); -*/ -char sqlite3ExprAffinity(Expr *pExpr){ - if( pExpr->op==TK_AS ){ - return sqlite3ExprAffinity(pExpr->pLeft); - } - if( pExpr->op==TK_SELECT ){ - return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr); - } - return pExpr->affinity; -} - -/* -** Return the default collation sequence for the expression pExpr. If -** there is no default collation type, return 0. -*/ -CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ - CollSeq *pColl = 0; - if( pExpr ){ - pColl = pExpr->pColl; - if( pExpr->op==TK_AS && !pColl ){ - return sqlite3ExprCollSeq(pParse, pExpr->pLeft); - } - } - if( sqlite3CheckCollSeq(pParse, pColl) ){ - pColl = 0; - } - return pColl; -} - -/* -** pExpr is the left operand of a comparison operator. aff2 is the -** type affinity of the right operand. This routine returns the -** type affinity that should be used for the comparison operator. -*/ -char sqlite3CompareAffinity(Expr *pExpr, char aff2){ - char aff1 = sqlite3ExprAffinity(pExpr); - if( aff1 && aff2 ){ - /* Both sides of the comparison are columns. If one has numeric or - ** integer affinity, use that. Otherwise use no affinity. - */ - if( aff1==SQLITE_AFF_INTEGER || aff2==SQLITE_AFF_INTEGER ){ - return SQLITE_AFF_INTEGER; - }else if( aff1==SQLITE_AFF_NUMERIC || aff2==SQLITE_AFF_NUMERIC ){ - return SQLITE_AFF_NUMERIC; - }else{ - return SQLITE_AFF_NONE; - } - }else if( !aff1 && !aff2 ){ - /* Neither side of the comparison is a column. Compare the - ** results directly. - */ - /* return SQLITE_AFF_NUMERIC; // Ticket #805 */ - return SQLITE_AFF_NONE; - }else{ - /* One side is a column, the other is not. Use the columns affinity. */ - return (aff1 + aff2); - } -} - -/* -** pExpr is a comparison operator. Return the type affinity that should -** be applied to both operands prior to doing the comparison. -*/ -static char comparisonAffinity(Expr *pExpr){ - char aff; - assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || - pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || - pExpr->op==TK_NE ); - assert( pExpr->pLeft ); - aff = sqlite3ExprAffinity(pExpr->pLeft); - if( pExpr->pRight ){ - aff = sqlite3CompareAffinity(pExpr->pRight, aff); - } - else if( pExpr->pSelect ){ - aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff); - } - else if( !aff ){ - aff = SQLITE_AFF_NUMERIC; - } - return aff; -} - -/* -** pExpr is a comparison expression, eg. '=', '<', IN(...) etc. -** idx_affinity is the affinity of an indexed column. Return true -** if the index with affinity idx_affinity may be used to implement -** the comparison in pExpr. -*/ -int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){ - char aff = comparisonAffinity(pExpr); - return - (aff==SQLITE_AFF_NONE) || - (aff==SQLITE_AFF_NUMERIC && idx_affinity==SQLITE_AFF_INTEGER) || - (aff==SQLITE_AFF_INTEGER && idx_affinity==SQLITE_AFF_NUMERIC) || - (aff==idx_affinity); -} - -/* -** Return the P1 value that should be used for a binary comparison -** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2. -** If jumpIfNull is true, then set the low byte of the returned -** P1 value to tell the opcode to jump if either expression -** evaluates to NULL. -*/ -static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){ - char aff = sqlite3ExprAffinity(pExpr2); - return (((int)sqlite3CompareAffinity(pExpr1, aff))<<8)+(jumpIfNull?1:0); -} - -/* -** Return a pointer to the collation sequence that should be used by -** a binary comparison operator comparing pLeft and pRight. -** -** If the left hand expression has a collating sequence type, then it is -** used. Otherwise the collation sequence for the right hand expression -** is used, or the default (BINARY) if neither expression has a collating -** type. -*/ -static CollSeq* binaryCompareCollSeq(Parse *pParse, Expr *pLeft, Expr *pRight){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pLeft); - if( !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pRight); - } - return pColl; -} - -/* -** Generate code for a comparison operator. -*/ -static int codeCompare( - Parse *pParse, /* The parsing (and code generating) context */ - Expr *pLeft, /* The left operand */ - Expr *pRight, /* The right operand */ - int opcode, /* The comparison opcode */ - int dest, /* Jump here if true. */ - int jumpIfNull /* If true, jump if either operand is NULL */ -){ - int p1 = binaryCompareP1(pLeft, pRight, jumpIfNull); - CollSeq *p3 = binaryCompareCollSeq(pParse, pLeft, pRight); - return sqlite3VdbeOp3(pParse->pVdbe, opcode, p1, dest, (void*)p3, P3_COLLSEQ); -} - -/* -** Construct a new expression node and return a pointer to it. Memory -** for this node is obtained from sqliteMalloc(). The calling function -** is responsible for making sure the node eventually gets freed. -*/ -Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ - Expr *pNew; - pNew = sqliteMalloc( sizeof(Expr) ); - if( pNew==0 ){ - /* When malloc fails, we leak memory from pLeft and pRight */ - return 0; - } - pNew->op = op; - pNew->pLeft = pLeft; - pNew->pRight = pRight; - if( pToken ){ - assert( pToken->dyn==0 ); - pNew->span = pNew->token = *pToken; - }else if( pLeft && pRight ){ - sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span); - } - return pNew; -} - -/* -** Join two expressions using an AND operator. If either expression is -** NULL, then just return the other expression. -*/ -Expr *sqlite3ExprAnd(Expr *pLeft, Expr *pRight){ - if( pLeft==0 ){ - return pRight; - }else if( pRight==0 ){ - return pLeft; - }else{ - return sqlite3Expr(TK_AND, pLeft, pRight, 0); - } -} - -/* -** Set the Expr.span field of the given expression to span all -** text between the two given tokens. -*/ -void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ - assert( pRight!=0 ); - assert( pLeft!=0 ); - if( !sqlite3_malloc_failed && pRight->z && pLeft->z ){ - assert( pLeft->dyn==0 || pLeft->z[pLeft->n]==0 ); - if( pLeft->dyn==0 && pRight->dyn==0 ){ - pExpr->span.z = pLeft->z; - pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z); - }else{ - pExpr->span.z = 0; - } - } -} - -/* -** Construct a new expression node for a function with multiple -** arguments. -*/ -Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){ - Expr *pNew; - pNew = sqliteMalloc( sizeof(Expr) ); - if( pNew==0 ){ - /* sqlite3ExprListDelete(pList); // Leak pList when malloc fails */ - return 0; - } - pNew->op = TK_FUNCTION; - pNew->pList = pList; - if( pToken ){ - assert( pToken->dyn==0 ); - pNew->token = *pToken; - }else{ - pNew->token.z = 0; - } - pNew->span = pNew->token; - return pNew; -} - -/* -** Assign a variable number to an expression that encodes a wildcard -** in the original SQL statement. -** -** Wildcards consisting of a single "?" are assigned the next sequential -** variable number. -** -** Wildcards of the form "?nnn" are assigned the number "nnn". We make -** sure "nnn" is not too be to avoid a denial of service attack when -** the SQL statement comes from an external source. -** -** Wildcards of the form ":aaa" or "$aaa" are assigned the same number -** as the previous instance of the same wildcard. Or if this is the first -** instance of the wildcard, the next sequenial variable number is -** assigned. -*/ -void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ - Token *pToken; - if( pExpr==0 ) return; - pToken = &pExpr->token; - assert( pToken->n>=1 ); - assert( pToken->z!=0 ); - assert( pToken->z[0]!=0 ); - if( pToken->n==1 ){ - /* Wildcard of the form "?". Assign the next variable number */ - pExpr->iTable = ++pParse->nVar; - }else if( pToken->z[0]=='?' ){ - /* Wildcard of the form "?nnn". Convert "nnn" to an integer and - ** use it as the variable number */ - int i; - pExpr->iTable = i = atoi(&pToken->z[1]); - if( i<1 || i>SQLITE_MAX_VARIABLE_NUMBER ){ - sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", - SQLITE_MAX_VARIABLE_NUMBER); - } - if( i>pParse->nVar ){ - pParse->nVar = i; - } - }else{ - /* Wildcards of the form ":aaa" or "$aaa". Reuse the same variable - ** number as the prior appearance of the same name, or if the name - ** has never appeared before, reuse the same variable number - */ - int i, n; - n = pToken->n; - for(i=0; i<pParse->nVarExpr; i++){ - Expr *pE; - if( (pE = pParse->apVarExpr[i])!=0 - && pE->token.n==n - && memcmp(pE->token.z, pToken->z, n)==0 ){ - pExpr->iTable = pE->iTable; - break; - } - } - if( i>=pParse->nVarExpr ){ - pExpr->iTable = ++pParse->nVar; - if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ - pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; - pParse->apVarExpr = sqliteRealloc(pParse->apVarExpr, - pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) ); - } - if( !sqlite3_malloc_failed ){ - assert( pParse->apVarExpr!=0 ); - pParse->apVarExpr[pParse->nVarExpr++] = pExpr; - } - } - } -} - -/* -** Recursively delete an expression tree. -*/ -void sqlite3ExprDelete(Expr *p){ - if( p==0 ) return; - if( p->span.dyn ) sqliteFree((char*)p->span.z); - if( p->token.dyn ) sqliteFree((char*)p->token.z); - sqlite3ExprDelete(p->pLeft); - sqlite3ExprDelete(p->pRight); - sqlite3ExprListDelete(p->pList); - sqlite3SelectDelete(p->pSelect); - sqliteFree(p); -} - - -/* -** The following group of routines make deep copies of expressions, -** expression lists, ID lists, and select statements. The copies can -** be deleted (by being passed to their respective ...Delete() routines) -** without effecting the originals. -** -** The expression list, ID, and source lists return by sqlite3ExprListDup(), -** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded -** by subsequent calls to sqlite*ListAppend() routines. -** -** Any tables that the SrcList might point to are not duplicated. -*/ -Expr *sqlite3ExprDup(Expr *p){ - Expr *pNew; - if( p==0 ) return 0; - pNew = sqliteMallocRaw( sizeof(*p) ); - if( pNew==0 ) return 0; - memcpy(pNew, p, sizeof(*pNew)); - if( p->token.z!=0 ){ - pNew->token.z = sqliteStrDup(p->token.z); - pNew->token.dyn = 1; - }else{ - assert( pNew->token.z==0 ); - } - pNew->span.z = 0; - pNew->pLeft = sqlite3ExprDup(p->pLeft); - pNew->pRight = sqlite3ExprDup(p->pRight); - pNew->pList = sqlite3ExprListDup(p->pList); - pNew->pSelect = sqlite3SelectDup(p->pSelect); - return pNew; -} -void sqlite3TokenCopy(Token *pTo, Token *pFrom){ - if( pTo->dyn ) sqliteFree((char*)pTo->z); - if( pFrom->z ){ - pTo->n = pFrom->n; - pTo->z = sqliteStrNDup(pFrom->z, pFrom->n); - pTo->dyn = 1; - }else{ - pTo->z = 0; - } -} -ExprList *sqlite3ExprListDup(ExprList *p){ - ExprList *pNew; - struct ExprList_item *pItem, *pOldItem; - int i; - if( p==0 ) return 0; - pNew = sqliteMalloc( sizeof(*pNew) ); - if( pNew==0 ) return 0; - pNew->nExpr = pNew->nAlloc = p->nExpr; - pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) ); - if( pItem==0 ){ - sqliteFree(pNew); - return 0; - } - pOldItem = p->a; - for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){ - Expr *pNewExpr, *pOldExpr; - pItem->pExpr = pNewExpr = sqlite3ExprDup(pOldExpr = pOldItem->pExpr); - if( pOldExpr->span.z!=0 && pNewExpr ){ - /* Always make a copy of the span for top-level expressions in the - ** expression list. The logic in SELECT processing that determines - ** the names of columns in the result set needs this information */ - sqlite3TokenCopy(&pNewExpr->span, &pOldExpr->span); - } - assert( pNewExpr==0 || pNewExpr->span.z!=0 - || pOldExpr->span.z==0 || sqlite3_malloc_failed ); - pItem->zName = sqliteStrDup(pOldItem->zName); - pItem->sortOrder = pOldItem->sortOrder; - pItem->isAgg = pOldItem->isAgg; - pItem->done = 0; - } - return pNew; -} -SrcList *sqlite3SrcListDup(SrcList *p){ - SrcList *pNew; - int i; - int nByte; - if( p==0 ) return 0; - nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); - pNew = sqliteMallocRaw( nByte ); - if( pNew==0 ) return 0; - pNew->nSrc = pNew->nAlloc = p->nSrc; - for(i=0; i<p->nSrc; i++){ - struct SrcList_item *pNewItem = &pNew->a[i]; - struct SrcList_item *pOldItem = &p->a[i]; - pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase); - pNewItem->zName = sqliteStrDup(pOldItem->zName); - pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias); - pNewItem->jointype = pOldItem->jointype; - pNewItem->iCursor = pOldItem->iCursor; - pNewItem->pTab = 0; - pNewItem->pSelect = sqlite3SelectDup(pOldItem->pSelect); - pNewItem->pOn = sqlite3ExprDup(pOldItem->pOn); - pNewItem->pUsing = sqlite3IdListDup(pOldItem->pUsing); - } - return pNew; -} -IdList *sqlite3IdListDup(IdList *p){ - IdList *pNew; - int i; - if( p==0 ) return 0; - pNew = sqliteMallocRaw( sizeof(*pNew) ); - if( pNew==0 ) return 0; - pNew->nId = pNew->nAlloc = p->nId; - pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) ); - if( pNew->a==0 ) return 0; - for(i=0; i<p->nId; i++){ - struct IdList_item *pNewItem = &pNew->a[i]; - struct IdList_item *pOldItem = &p->a[i]; - pNewItem->zName = sqliteStrDup(pOldItem->zName); - pNewItem->idx = pOldItem->idx; - } - return pNew; -} -Select *sqlite3SelectDup(Select *p){ - Select *pNew; - if( p==0 ) return 0; - pNew = sqliteMallocRaw( sizeof(*p) ); - if( pNew==0 ) return 0; - pNew->isDistinct = p->isDistinct; - pNew->pEList = sqlite3ExprListDup(p->pEList); - pNew->pSrc = sqlite3SrcListDup(p->pSrc); - pNew->pWhere = sqlite3ExprDup(p->pWhere); - pNew->pGroupBy = sqlite3ExprListDup(p->pGroupBy); - pNew->pHaving = sqlite3ExprDup(p->pHaving); - pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy); - pNew->op = p->op; - pNew->pPrior = sqlite3SelectDup(p->pPrior); - pNew->nLimit = p->nLimit; - pNew->nOffset = p->nOffset; - pNew->zSelect = 0; - pNew->iLimit = -1; - pNew->iOffset = -1; - pNew->ppOpenTemp = 0; - return pNew; -} - - -/* -** Add a new element to the end of an expression list. If pList is -** initially NULL, then create a new expression list. -*/ -ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ - if( pList==0 ){ - pList = sqliteMalloc( sizeof(ExprList) ); - if( pList==0 ){ - /* sqlite3ExprDelete(pExpr); // Leak memory if malloc fails */ - return 0; - } - assert( pList->nAlloc==0 ); - } - if( pList->nAlloc<=pList->nExpr ){ - pList->nAlloc = pList->nAlloc*2 + 4; - pList->a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0])); - if( pList->a==0 ){ - /* sqlite3ExprDelete(pExpr); // Leak memory if malloc fails */ - pList->nExpr = pList->nAlloc = 0; - return pList; - } - } - assert( pList->a!=0 ); - if( pExpr || pName ){ - struct ExprList_item *pItem = &pList->a[pList->nExpr++]; - memset(pItem, 0, sizeof(*pItem)); - pItem->pExpr = pExpr; - pItem->zName = sqlite3NameFromToken(pName); - } - return pList; -} - -/* -** Delete an entire expression list. -*/ -void sqlite3ExprListDelete(ExprList *pList){ - int i; - struct ExprList_item *pItem; - if( pList==0 ) return; - assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) ); - assert( pList->nExpr<=pList->nAlloc ); - for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){ - sqlite3ExprDelete(pItem->pExpr); - sqliteFree(pItem->zName); - } - sqliteFree(pList->a); - sqliteFree(pList); -} - -/* -** Walk an expression tree. Return 1 if the expression is constant -** and 0 if it involves variables. -** -** For the purposes of this function, a double-quoted string (ex: "abc") -** is considered a variable but a single-quoted string (ex: 'abc') is -** a constant. -*/ -int sqlite3ExprIsConstant(Expr *p){ - switch( p->op ){ - case TK_ID: - case TK_COLUMN: - case TK_DOT: - case TK_FUNCTION: - return 0; - case TK_NULL: - case TK_STRING: - case TK_BLOB: - case TK_INTEGER: - case TK_FLOAT: - case TK_VARIABLE: - return 1; - default: { - if( p->pLeft && !sqlite3ExprIsConstant(p->pLeft) ) return 0; - if( p->pRight && !sqlite3ExprIsConstant(p->pRight) ) return 0; - if( p->pList ){ - int i; - for(i=0; i<p->pList->nExpr; i++){ - if( !sqlite3ExprIsConstant(p->pList->a[i].pExpr) ) return 0; - } - } - return p->pLeft!=0 || p->pRight!=0 || (p->pList && p->pList->nExpr>0); - } - } - return 0; -} - -/* -** If the given expression codes a constant integer that is small enough -** to fit in a 32-bit integer, return 1 and put the value of the integer -** in *pValue. If the expression is not an integer or if it is too big -** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. -*/ -int sqlite3ExprIsInteger(Expr *p, int *pValue){ - switch( p->op ){ - case TK_INTEGER: { - if( sqlite3GetInt32(p->token.z, pValue) ){ - return 1; - } - break; - } - case TK_STRING: { - const u8 *z = (u8*)p->token.z; - int n = p->token.n; - if( n>0 && z[0]=='-' ){ z++; n--; } - while( n>0 && *z && isdigit(*z) ){ z++; n--; } - if( n==0 && sqlite3GetInt32(p->token.z, pValue) ){ - return 1; - } - break; - } - case TK_UPLUS: { - return sqlite3ExprIsInteger(p->pLeft, pValue); - } - case TK_UMINUS: { - int v; - if( sqlite3ExprIsInteger(p->pLeft, &v) ){ - *pValue = -v; - return 1; - } - break; - } - default: break; - } - return 0; -} - -/* -** Return TRUE if the given string is a row-id column name. -*/ -int sqlite3IsRowid(const char *z){ - if( sqlite3StrICmp(z, "_ROWID_")==0 ) return 1; - if( sqlite3StrICmp(z, "ROWID")==0 ) return 1; - if( sqlite3StrICmp(z, "OID")==0 ) return 1; - return 0; -} - -/* -** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up -** that name in the set of source tables in pSrcList and make the pExpr -** expression node refer back to that source column. The following changes -** are made to pExpr: -** -** pExpr->iDb Set the index in db->aDb[] of the database holding -** the table. -** pExpr->iTable Set to the cursor number for the table obtained -** from pSrcList. -** pExpr->iColumn Set to the column number within the table. -** pExpr->op Set to TK_COLUMN. -** pExpr->pLeft Any expression this points to is deleted -** pExpr->pRight Any expression this points to is deleted. -** -** The pDbToken is the name of the database (the "X"). This value may be -** NULL meaning that name is of the form Y.Z or Z. Any available database -** can be used. The pTableToken is the name of the table (the "Y"). This -** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it -** means that the form of the name is Z and that columns from any table -** can be used. -** -** If the name cannot be resolved unambiguously, leave an error message -** in pParse and return non-zero. Return zero on success. -*/ -static int lookupName( - Parse *pParse, /* The parsing context */ - Token *pDbToken, /* Name of the database containing table, or NULL */ - Token *pTableToken, /* Name of table containing column, or NULL */ - Token *pColumnToken, /* Name of the column. */ - SrcList *pSrcList, /* List of tables used to resolve column names */ - ExprList *pEList, /* List of expressions used to resolve "AS" */ - Expr *pExpr /* Make this EXPR node point to the selected column */ -){ - char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */ - char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */ - char *zCol = 0; /* Name of the column. The "Z" */ - int i, j; /* Loop counters */ - int cnt = 0; /* Number of matching column names */ - int cntTab = 0; /* Number of matching table names */ - sqlite3 *db = pParse->db; /* The database */ - - assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ - zDb = sqlite3NameFromToken(pDbToken); - zTab = sqlite3NameFromToken(pTableToken); - zCol = sqlite3NameFromToken(pColumnToken); - if( sqlite3_malloc_failed ){ - return 1; /* Leak memory (zDb and zTab) if malloc fails */ - } - assert( zTab==0 || pEList==0 ); - - pExpr->iTable = -1; - for(i=0; i<pSrcList->nSrc; i++){ - struct SrcList_item *pItem = &pSrcList->a[i]; - Table *pTab = pItem->pTab; - Column *pCol; - - if( pTab==0 ) continue; - assert( pTab->nCol>0 ); - if( zTab ){ - if( pItem->zAlias ){ - char *zTabName = pItem->zAlias; - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - }else{ - char *zTabName = pTab->zName; - if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ - continue; - } - } - } - if( 0==(cntTab++) ){ - pExpr->iTable = pItem->iCursor; - pExpr->iDb = pTab->iDb; - } - for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - cnt++; - pExpr->iTable = pItem->iCursor; - pExpr->iDb = pTab->iDb; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : j; - pExpr->affinity = pTab->aCol[j].affinity; - pExpr->pColl = pTab->aCol[j].pColl; - break; - } - } - } - - /* If we have not already resolved the name, then maybe - ** it is a new.* or old.* trigger argument reference - */ - if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ - TriggerStack *pTriggerStack = pParse->trigStack; - Table *pTab = 0; - if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){ - pExpr->iTable = pTriggerStack->newIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab) == 0 ){ - pExpr->iTable = pTriggerStack->oldIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - } - - if( pTab ){ - int j; - Column *pCol = pTab->aCol; - - pExpr->iDb = pTab->iDb; - cntTab++; - for(j=0; j < pTab->nCol; j++, pCol++) { - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - cnt++; - pExpr->iColumn = j==pTab->iPKey ? -1 : j; - pExpr->affinity = pTab->aCol[j].affinity; - pExpr->pColl = pTab->aCol[j].pColl; - break; - } - } - } - } - - /* - ** Perhaps the name is a reference to the ROWID - */ - if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){ - cnt = 1; - pExpr->iColumn = -1; - pExpr->affinity = SQLITE_AFF_INTEGER; - } - - /* - ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z - ** might refer to an result-set alias. This happens, for example, when - ** we are resolving names in the WHERE clause of the following command: - ** - ** SELECT a+b AS x FROM table WHERE x<10; - ** - ** In cases like this, replace pExpr with a copy of the expression that - ** forms the result set entry ("a+b" in the example) and return immediately. - ** Note that the expression in the result set should have already been - ** resolved by the time the WHERE clause is resolved. - */ - if( cnt==0 && pEList!=0 ){ - for(j=0; j<pEList->nExpr; j++){ - char *zAs = pEList->a[j].zName; - if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ - assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - pExpr->op = TK_AS; - pExpr->iColumn = j; - pExpr->pLeft = sqlite3ExprDup(pEList->a[j].pExpr); - sqliteFree(zCol); - assert( zTab==0 && zDb==0 ); - return 0; - } - } - } - - /* - ** If X and Y are NULL (in other words if only the column name Z is - ** supplied) and the value of Z is enclosed in double-quotes, then - ** Z is a string literal if it doesn't match any column names. In that - ** case, we need to return right away and not make any changes to - ** pExpr. - */ - if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){ - sqliteFree(zCol); - return 0; - } - - /* - ** cnt==0 means there was not match. cnt>1 means there were two or - ** more matches. Either way, we have an error. - */ - if( cnt!=1 ){ - char *z = 0; - char *zErr; - zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s"; - if( zDb ){ - sqlite3SetString(&z, zDb, ".", zTab, ".", zCol, 0); - }else if( zTab ){ - sqlite3SetString(&z, zTab, ".", zCol, 0); - }else{ - z = sqliteStrDup(zCol); - } - sqlite3ErrorMsg(pParse, zErr, z); - sqliteFree(z); - } - - /* Clean up and return - */ - sqliteFree(zDb); - sqliteFree(zTab); - sqliteFree(zCol); - sqlite3ExprDelete(pExpr->pLeft); - pExpr->pLeft = 0; - sqlite3ExprDelete(pExpr->pRight); - pExpr->pRight = 0; - pExpr->op = TK_COLUMN; - sqlite3AuthRead(pParse, pExpr, pSrcList); - return cnt!=1; -} - -/* -** This routine walks an expression tree and resolves references to -** table columns. Nodes of the form ID.ID or ID resolve into an -** index to the table in the table list and a column offset. The -** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable -** value is changed to the index of the referenced table in pTabList -** plus the "base" value. The base value will ultimately become the -** VDBE cursor number for a cursor that is pointing into the referenced -** table. The Expr.iColumn value is changed to the index of the column -** of the referenced table. The Expr.iColumn value for the special -** ROWID column is -1. Any INTEGER PRIMARY KEY column is tried as an -** alias for ROWID. -** -** We also check for instances of the IN operator. IN comes in two -** forms: -** -** expr IN (exprlist) -** and -** expr IN (SELECT ...) -** -** The first form is handled by creating a set holding the list -** of allowed values. The second form causes the SELECT to generate -** a temporary table. -** -** This routine also looks for scalar SELECTs that are part of an expression. -** If it finds any, it generates code to write the value of that select -** into a memory cell. -** -** Unknown columns or tables provoke an error. The function returns -** the number of errors seen and leaves an error message on pParse->zErrMsg. -*/ -int sqlite3ExprResolveIds( - Parse *pParse, /* The parser context */ - SrcList *pSrcList, /* List of tables used to resolve column names */ - ExprList *pEList, /* List of expressions used to resolve "AS" */ - Expr *pExpr /* The expression to be analyzed. */ -){ - int i; - - if( pExpr==0 || pSrcList==0 ) return 0; - for(i=0; i<pSrcList->nSrc; i++){ - assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab ); - } - switch( pExpr->op ){ - /* Double-quoted strings (ex: "abc") are used as identifiers if - ** possible. Otherwise they remain as strings. Single-quoted - ** strings (ex: 'abc') are always string literals. - */ - case TK_STRING: { - if( pExpr->token.z[0]=='\'' ) break; - /* Fall thru into the TK_ID case if this is a double-quoted string */ - } - /* A lone identifier is the name of a columnd. - */ - case TK_ID: { - if( lookupName(pParse, 0, 0, &pExpr->token, pSrcList, pEList, pExpr) ){ - return 1; - } - break; - } - - /* A table name and column name: ID.ID - ** Or a database, table and column: ID.ID.ID - */ - case TK_DOT: { - Token *pColumn; - Token *pTable; - Token *pDb; - Expr *pRight; - - pRight = pExpr->pRight; - if( pRight->op==TK_ID ){ - pDb = 0; - pTable = &pExpr->pLeft->token; - pColumn = &pRight->token; - }else{ - assert( pRight->op==TK_DOT ); - pDb = &pExpr->pLeft->token; - pTable = &pRight->pLeft->token; - pColumn = &pRight->pRight->token; - } - if( lookupName(pParse, pDb, pTable, pColumn, pSrcList, 0, pExpr) ){ - return 1; - } - break; - } - - case TK_IN: { - char affinity; - Vdbe *v = sqlite3GetVdbe(pParse); - KeyInfo keyInfo; - int addr; /* Address of OP_OpenTemp instruction */ - - if( v==0 ) return 1; - if( sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ - return 1; - } - affinity = sqlite3ExprAffinity(pExpr->pLeft); - - /* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)' - ** expression it is handled the same way. A temporary table is - ** filled with single-field index keys representing the results - ** from the SELECT or the <exprlist>. - ** - ** If the 'x' expression is a column value, or the SELECT... - ** statement returns a column value, then the affinity of that - ** column is used to build the index keys. If both 'x' and the - ** SELECT... statement are columns, then numeric affinity is used - ** if either column has NUMERIC or INTEGER affinity. If neither - ** 'x' nor the SELECT... statement are columns, then numeric affinity - ** is used. - */ - pExpr->iTable = pParse->nTab++; - addr = sqlite3VdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 0); - memset(&keyInfo, 0, sizeof(keyInfo)); - keyInfo.nField = 1; - sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1); - - if( pExpr->pSelect ){ - /* Case 1: expr IN (SELECT ...) - ** - ** Generate code to write the results of the select into the temporary - ** table allocated and opened above. - */ - int iParm = pExpr->iTable + (((int)affinity)<<16); - ExprList *pEList; - assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); - sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0); - pEList = pExpr->pSelect->pEList; - if( pEList && pEList->nExpr>0 ){ - keyInfo.aColl[0] = binaryCompareCollSeq(pParse, pExpr->pLeft, - pEList->a[0].pExpr); - } - }else if( pExpr->pList ){ - /* Case 2: expr IN (exprlist) - ** - ** For each expression, build an index key from the evaluation and - ** store it in the temporary table. If <expr> is a column, then use - ** that columns affinity when building index keys. If <expr> is not - ** a column, use numeric affinity. - */ - int i; - if( !affinity ){ - affinity = SQLITE_AFF_NUMERIC; - } - keyInfo.aColl[0] = pExpr->pLeft->pColl; - - /* Loop through each expression in <exprlist>. */ - for(i=0; i<pExpr->pList->nExpr; i++){ - Expr *pE2 = pExpr->pList->a[i].pExpr; - - /* Check that the expression is constant and valid. */ - if( !sqlite3ExprIsConstant(pE2) ){ - sqlite3ErrorMsg(pParse, - "right-hand side of IN operator must be constant"); - return 1; - } - if( sqlite3ExprCheck(pParse, pE2, 0, 0) ){ - return 1; - } - - /* Evaluate the expression and insert it into the temp table */ - sqlite3ExprCode(pParse, pE2); - sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutStrKey, pExpr->iTable, 0); - } - } - sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO); - - break; - } - - case TK_SELECT: { - /* This has to be a scalar SELECT. Generate code to put the - ** value of this select in a memory cell and record the number - ** of the memory cell in iColumn. - */ - pExpr->iColumn = pParse->nMem++; - if(sqlite3Select(pParse, pExpr->pSelect, SRT_Mem,pExpr->iColumn,0,0,0,0)){ - return 1; - } - break; - } - - /* For all else, just recursively walk the tree */ - default: { - if( pExpr->pLeft - && sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ - return 1; - } - if( pExpr->pRight - && sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pRight) ){ - return 1; - } - if( pExpr->pList ){ - int i; - ExprList *pList = pExpr->pList; - for(i=0; i<pList->nExpr; i++){ - Expr *pArg = pList->a[i].pExpr; - if( sqlite3ExprResolveIds(pParse, pSrcList, pEList, pArg) ){ - return 1; - } - } - } - } - } - return 0; -} - -/* -** pExpr is a node that defines a function of some kind. It might -** be a syntactic function like "count(x)" or it might be a function -** that implements an operator, like "a LIKE b". -** -** This routine makes *pzName point to the name of the function and -** *pnName hold the number of characters in the function name. -*/ -static void getFunctionName(Expr *pExpr, const char **pzName, int *pnName){ - switch( pExpr->op ){ - case TK_FUNCTION: { - *pzName = pExpr->token.z; - *pnName = pExpr->token.n; - break; - } - case TK_LIKE: { - *pzName = "like"; - *pnName = 4; - break; - } - case TK_GLOB: { - *pzName = "glob"; - *pnName = 4; - break; - } - default: { - *pzName = "can't happen"; - *pnName = 12; - break; - } - } -} - -/* -** Error check the functions in an expression. Make sure all -** function names are recognized and all functions have the correct -** number of arguments. Leave an error message in pParse->zErrMsg -** if anything is amiss. Return the number of errors. -** -** if pIsAgg is not null and this expression is an aggregate function -** (like count(*) or max(value)) then write a 1 into *pIsAgg. -*/ -int sqlite3ExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ - int nErr = 0; - if( pExpr==0 ) return 0; - switch( pExpr->op ){ - case TK_GLOB: - case TK_LIKE: - case TK_FUNCTION: { - int n = pExpr->pList ? pExpr->pList->nExpr : 0; /* Number of arguments */ - int no_such_func = 0; /* True if no such function exists */ - int wrong_num_args = 0; /* True if wrong number of arguments */ - int is_agg = 0; /* True if is an aggregate function */ - int i; - int nId; /* Number of characters in function name */ - const char *zId; /* The function name. */ - FuncDef *pDef; - int enc = pParse->db->enc; - - getFunctionName(pExpr, &zId, &nId); - pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); - if( pDef==0 ){ - pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0); - if( pDef==0 ){ - no_such_func = 1; - }else{ - wrong_num_args = 1; - } - }else{ - is_agg = pDef->xFunc==0; - } - if( is_agg && !allowAgg ){ - sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId, zId); - nErr++; - is_agg = 0; - }else if( no_such_func ){ - sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); - nErr++; - }else if( wrong_num_args ){ - sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", - nId, zId); - nErr++; - } - if( is_agg ){ - pExpr->op = TK_AGG_FUNCTION; - if( pIsAgg ) *pIsAgg = 1; - } - for(i=0; nErr==0 && i<n; i++){ - nErr = sqlite3ExprCheck(pParse, pExpr->pList->a[i].pExpr, - allowAgg && !is_agg, pIsAgg); - } - /* FIX ME: Compute pExpr->affinity based on the expected return - ** type of the function - */ - } - default: { - if( pExpr->pLeft ){ - nErr = sqlite3ExprCheck(pParse, pExpr->pLeft, allowAgg, pIsAgg); - } - if( nErr==0 && pExpr->pRight ){ - nErr = sqlite3ExprCheck(pParse, pExpr->pRight, allowAgg, pIsAgg); - } - if( nErr==0 && pExpr->pList ){ - int n = pExpr->pList->nExpr; - int i; - for(i=0; nErr==0 && i<n; i++){ - Expr *pE2 = pExpr->pList->a[i].pExpr; - nErr = sqlite3ExprCheck(pParse, pE2, allowAgg, pIsAgg); - } - } - break; - } - } - return nErr; -} - -/* -** Call sqlite3ExprResolveIds() followed by sqlite3ExprCheck(). -** -** This routine is provided as a convenience since it is very common -** to call ResolveIds() and Check() back to back. -*/ -int sqlite3ExprResolveAndCheck( - Parse *pParse, /* The parser context */ - SrcList *pSrcList, /* List of tables used to resolve column names */ - ExprList *pEList, /* List of expressions used to resolve "AS" */ - Expr *pExpr, /* The expression to be analyzed. */ - int allowAgg, /* True to allow aggregate expressions */ - int *pIsAgg /* Set to TRUE if aggregates are found */ -){ - if( pExpr==0 ) return 0; - if( sqlite3ExprResolveIds(pParse,pSrcList,pEList,pExpr) ){ - return 1; - } - return sqlite3ExprCheck(pParse, pExpr, allowAgg, pIsAgg); -} - -/* -** Generate an instruction that will put the integer describe by -** text z[0..n-1] on the stack. -*/ -static void codeInteger(Vdbe *v, const char *z, int n){ - int i; - if( sqlite3GetInt32(z, &i) ){ - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - }else if( sqlite3FitsIn64Bits(z) ){ - sqlite3VdbeOp3(v, OP_Integer, 0, 0, z, n); - }else{ - sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n); - } -} - -/* -** Generate code into the current Vdbe to evaluate the given -** expression and leave the result on the top of stack. -** -** This code depends on the fact that certain token values (ex: TK_EQ) -** are the same as opcode values (ex: OP_Eq) that implement the corresponding -** operation. Special comments in vdbe.c and the mkopcodeh.awk script in -** the make process cause these values to align. Assert()s in the code -** below verify that the numbers are aligned correctly. -*/ -void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ - Vdbe *v = pParse->pVdbe; - int op; - if( v==0 || pExpr==0 ) return; - op = pExpr->op; - switch( op ){ - case TK_COLUMN: { - if( pParse->useAgg ){ - sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); - }else if( pExpr->iColumn>=0 ){ - sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn); -#ifndef NDEBUG - if( pExpr->span.z && pExpr->span.n>0 && pExpr->span.n<100 ){ - VdbeComment((v, "# %T", &pExpr->span)); - } -#endif - }else{ - sqlite3VdbeAddOp(v, OP_Recno, pExpr->iTable, 0); - } - break; - } - case TK_INTEGER: { - codeInteger(v, pExpr->token.z, pExpr->token.n); - break; - } - case TK_FLOAT: - case TK_STRING: { - assert( TK_FLOAT==OP_Real ); - assert( TK_STRING==OP_String8 ); - sqlite3VdbeOp3(v, op, 0, 0, pExpr->token.z, pExpr->token.n); - sqlite3VdbeDequoteP3(v, -1); - break; - } - case TK_BLOB: { - assert( TK_BLOB==OP_HexBlob ); - sqlite3VdbeOp3(v, op, 0, 0, pExpr->token.z+1, pExpr->token.n-1); - sqlite3VdbeDequoteP3(v, -1); - break; - } - case TK_NULL: { - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - break; - } - case TK_VARIABLE: { - sqlite3VdbeAddOp(v, OP_Variable, pExpr->iTable, 0); - if( pExpr->token.n>1 ){ - sqlite3VdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n); - } - break; - } - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3ExprCode(pParse, pExpr->pRight); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0); - break; - } - case TK_AND: - case TK_OR: - case TK_PLUS: - case TK_STAR: - case TK_MINUS: - case TK_REM: - case TK_BITAND: - case TK_BITOR: - case TK_SLASH: - case TK_LSHIFT: - case TK_RSHIFT: - case TK_CONCAT: { - assert( TK_AND==OP_And ); - assert( TK_OR==OP_Or ); - assert( TK_PLUS==OP_Add ); - assert( TK_MINUS==OP_Subtract ); - assert( TK_REM==OP_Remainder ); - assert( TK_BITAND==OP_BitAnd ); - assert( TK_BITOR==OP_BitOr ); - assert( TK_SLASH==OP_Divide ); - assert( TK_LSHIFT==OP_ShiftLeft ); - assert( TK_RSHIFT==OP_ShiftRight ); - assert( TK_CONCAT==OP_Concat ); - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3ExprCode(pParse, pExpr->pRight); - sqlite3VdbeAddOp(v, op, 0, 0); - break; - } - case TK_UMINUS: { - Expr *pLeft = pExpr->pLeft; - assert( pLeft ); - if( pLeft->op==TK_FLOAT || pLeft->op==TK_INTEGER ){ - Token *p = &pLeft->token; - char *z = sqliteMalloc( p->n + 2 ); - sprintf(z, "-%.*s", p->n, p->z); - if( pLeft->op==TK_FLOAT ){ - sqlite3VdbeOp3(v, OP_Real, 0, 0, z, p->n+1); - }else{ - codeInteger(v, z, p->n+1); - } - sqliteFree(z); - break; - } - /* Fall through into TK_NOT */ - } - case TK_BITNOT: - case TK_NOT: { - assert( TK_BITNOT==OP_BitNot ); - assert( TK_NOT==OP_Not ); - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3VdbeAddOp(v, op, 0, 0); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - int dest; - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - sqlite3VdbeAddOp(v, OP_Integer, 1, 0); - sqlite3ExprCode(pParse, pExpr->pLeft); - dest = sqlite3VdbeCurrentAddr(v) + 2; - sqlite3VdbeAddOp(v, op, 1, dest); - sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); - break; - } - case TK_AGG_FUNCTION: { - sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); - break; - } - case TK_GLOB: - case TK_LIKE: - case TK_FUNCTION: { - ExprList *pList = pExpr->pList; - int nExpr = pList ? pList->nExpr : 0; - FuncDef *pDef; - int nId; - const char *zId; - int p2 = 0; - int i; - u8 enc = pParse->db->enc; - CollSeq *pColl = 0; - getFunctionName(pExpr, &zId, &nId); - pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0); - assert( pDef!=0 ); - nExpr = sqlite3ExprCodeExprList(pParse, pList); - for(i=0; i<nExpr && i<32; i++){ - if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){ - p2 |= (1<<i); - } - if( pDef->needCollSeq && !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); - } - } - if( pDef->needCollSeq ){ - if( !pColl ) pColl = pParse->db->pDfltColl; - sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ); - } - sqlite3VdbeOp3(v, OP_Function, nExpr, p2, (char*)pDef, P3_FUNCDEF); - break; - } - case TK_SELECT: { - sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0); - VdbeComment((v, "# load subquery result")); - break; - } - case TK_IN: { - int addr; - char affinity; - - /* Figure out the affinity to use to create a key from the results - ** of the expression. affinityStr stores a static string suitable for - ** P3 of OP_MakeRecord. - */ - affinity = comparisonAffinity(pExpr); - - sqlite3VdbeAddOp(v, OP_Integer, 1, 0); - - /* Code the <expr> from "<expr> IN (...)". The temporary table - ** pExpr->iTable contains the values that make up the (...) set. - */ - sqlite3ExprCode(pParse, pExpr->pLeft); - addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+4); /* addr + 0 */ - sqlite3VdbeAddOp(v, OP_Pop, 2, 0); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, addr+7); - sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); /* addr + 4 */ - sqlite3VdbeAddOp(v, OP_Found, pExpr->iTable, addr+7); - sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); /* addr + 6 */ - - break; - } - case TK_BETWEEN: { - Expr *pLeft = pExpr->pLeft; - struct ExprList_item *pLItem = pExpr->pList->a; - Expr *pRight = pLItem->pExpr; - sqlite3ExprCode(pParse, pLeft); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3ExprCode(pParse, pRight); - codeCompare(pParse, pLeft, pRight, OP_Ge, 0, 0); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - pLItem++; - pRight = pLItem->pExpr; - sqlite3ExprCode(pParse, pRight); - codeCompare(pParse, pLeft, pRight, OP_Le, 0, 0); - sqlite3VdbeAddOp(v, OP_And, 0, 0); - break; - } - case TK_UPLUS: - case TK_AS: { - sqlite3ExprCode(pParse, pExpr->pLeft); - break; - } - case TK_CASE: { - int expr_end_label; - int jumpInst; - int addr; - int nExpr; - int i; - ExprList *pEList; - struct ExprList_item *aListelem; - - assert(pExpr->pList); - assert((pExpr->pList->nExpr % 2) == 0); - assert(pExpr->pList->nExpr > 0); - pEList = pExpr->pList; - aListelem = pEList->a; - nExpr = pEList->nExpr; - expr_end_label = sqlite3VdbeMakeLabel(v); - if( pExpr->pLeft ){ - sqlite3ExprCode(pParse, pExpr->pLeft); - } - for(i=0; i<nExpr; i=i+2){ - sqlite3ExprCode(pParse, aListelem[i].pExpr); - if( pExpr->pLeft ){ - sqlite3VdbeAddOp(v, OP_Dup, 1, 1); - jumpInst = codeCompare(pParse, pExpr->pLeft, aListelem[i].pExpr, - OP_Ne, 0, 1); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - }else{ - jumpInst = sqlite3VdbeAddOp(v, OP_IfNot, 1, 0); - } - sqlite3ExprCode(pParse, aListelem[i+1].pExpr); - sqlite3VdbeAddOp(v, OP_Goto, 0, expr_end_label); - addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeChangeP2(v, jumpInst, addr); - } - if( pExpr->pLeft ){ - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - } - if( pExpr->pRight ){ - sqlite3ExprCode(pParse, pExpr->pRight); - }else{ - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - } - sqlite3VdbeResolveLabel(v, expr_end_label); - break; - } - case TK_RAISE: { - if( !pParse->trigStack ){ - sqlite3ErrorMsg(pParse, - "RAISE() may only be used within a trigger-program"); - return; - } - if( pExpr->iColumn!=OE_Ignore ){ - assert( pExpr->iColumn==OE_Rollback || - pExpr->iColumn == OE_Abort || - pExpr->iColumn == OE_Fail ); - sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, - pExpr->token.z, pExpr->token.n); - sqlite3VdbeDequoteP3(v, -1); - } else { - assert( pExpr->iColumn == OE_Ignore ); - sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump); - VdbeComment((v, "# raise(IGNORE)")); - } - } - break; - } -} - -/* -** Generate code that pushes the value of every element of the given -** expression list onto the stack. -** -** Return the number of elements pushed onto the stack. -*/ -int sqlite3ExprCodeExprList( - Parse *pParse, /* Parsing context */ - ExprList *pList /* The expression list to be coded */ -){ - struct ExprList_item *pItem; - int i, n; - Vdbe *v; - if( pList==0 ) return 0; - v = sqlite3GetVdbe(pParse); - n = pList->nExpr; - for(pItem=pList->a, i=0; i<n; i++, pItem++){ - sqlite3ExprCode(pParse, pItem->pExpr); - } - return n; -} - -/* -** Generate code for a boolean expression such that a jump is made -** to the label "dest" if the expression is true but execution -** continues straight thru if the expression is false. -** -** If the expression evaluates to NULL (neither true nor false), then -** take the jump if the jumpIfNull flag is true. -** -** This code depends on the fact that certain token values (ex: TK_EQ) -** are the same as opcode values (ex: OP_Eq) that implement the corresponding -** operation. Special comments in vdbe.c and the mkopcodeh.awk script in -** the make process cause these values to align. Assert()s in the code -** below verify that the numbers are aligned correctly. -*/ -void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ - Vdbe *v = pParse->pVdbe; - int op = 0; - if( v==0 || pExpr==0 ) return; - op = pExpr->op; - switch( op ){ - case TK_AND: { - int d2 = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull); - sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3VdbeResolveLabel(v, d2); - break; - } - case TK_OR: { - sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); - sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - break; - } - case TK_NOT: { - sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); - break; - } - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3ExprCode(pParse, pExpr->pRight); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3VdbeAddOp(v, op, 1, dest); - break; - } - case TK_BETWEEN: { - /* The expression "x BETWEEN y AND z" is implemented as: - ** - ** 1 IF (x < y) GOTO 3 - ** 2 IF (x <= z) GOTO <dest> - ** 3 ... - */ - int addr; - Expr *pLeft = pExpr->pLeft; - Expr *pRight = pExpr->pList->a[0].pExpr; - sqlite3ExprCode(pParse, pLeft); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3ExprCode(pParse, pRight); - addr = codeCompare(pParse, pLeft, pRight, OP_Lt, 0, !jumpIfNull); - - pRight = pExpr->pList->a[1].pExpr; - sqlite3ExprCode(pParse, pRight); - codeCompare(pParse, pLeft, pRight, OP_Le, dest, jumpIfNull); - - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - break; - } - default: { - sqlite3ExprCode(pParse, pExpr); - sqlite3VdbeAddOp(v, OP_If, jumpIfNull, dest); - break; - } - } -} - -/* -** Generate code for a boolean expression such that a jump is made -** to the label "dest" if the expression is false but execution -** continues straight thru if the expression is true. -** -** If the expression evaluates to NULL (neither true nor false) then -** jump if jumpIfNull is true or fall through if jumpIfNull is false. -*/ -void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ - Vdbe *v = pParse->pVdbe; - int op = 0; - if( v==0 || pExpr==0 ) return; - - /* The value of pExpr->op and op are related as follows: - ** - ** pExpr->op op - ** --------- ---------- - ** TK_ISNULL OP_NotNull - ** TK_NOTNULL OP_IsNull - ** TK_NE OP_Eq - ** TK_EQ OP_Ne - ** TK_GT OP_Le - ** TK_LE OP_Gt - ** TK_GE OP_Lt - ** TK_LT OP_Ge - ** - ** For other values of pExpr->op, op is undefined and unused. - ** The value of TK_ and OP_ constants are arranged such that we - ** can compute the mapping above using the following expression. - ** Assert()s verify that the computation is correct. - */ - op = ((pExpr->op+(TK_ISNULL&1))^1)-(TK_ISNULL&1); - - /* Verify correct alignment of TK_ and OP_ constants - */ - assert( pExpr->op!=TK_ISNULL || op==OP_NotNull ); - assert( pExpr->op!=TK_NOTNULL || op==OP_IsNull ); - assert( pExpr->op!=TK_NE || op==OP_Eq ); - assert( pExpr->op!=TK_EQ || op==OP_Ne ); - assert( pExpr->op!=TK_LT || op==OP_Ge ); - assert( pExpr->op!=TK_LE || op==OP_Gt ); - assert( pExpr->op!=TK_GT || op==OP_Le ); - assert( pExpr->op!=TK_GE || op==OP_Lt ); - - switch( pExpr->op ){ - case TK_AND: { - sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); - sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - break; - } - case TK_OR: { - int d2 = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull); - sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3VdbeResolveLabel(v, d2); - break; - } - case TK_NOT: { - sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); - break; - } - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3ExprCode(pParse, pExpr->pRight); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - sqlite3ExprCode(pParse, pExpr->pLeft); - sqlite3VdbeAddOp(v, op, 1, dest); - break; - } - case TK_BETWEEN: { - /* The expression is "x BETWEEN y AND z". It is implemented as: - ** - ** 1 IF (x >= y) GOTO 3 - ** 2 GOTO <dest> - ** 3 IF (x > z) GOTO <dest> - */ - int addr; - Expr *pLeft = pExpr->pLeft; - Expr *pRight = pExpr->pList->a[0].pExpr; - sqlite3ExprCode(pParse, pLeft); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - sqlite3ExprCode(pParse, pRight); - addr = sqlite3VdbeCurrentAddr(v); - codeCompare(pParse, pLeft, pRight, OP_Ge, addr+3, !jumpIfNull); - - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, dest); - pRight = pExpr->pList->a[1].pExpr; - sqlite3ExprCode(pParse, pRight); - codeCompare(pParse, pLeft, pRight, OP_Gt, dest, jumpIfNull); - break; - } - default: { - sqlite3ExprCode(pParse, pExpr); - sqlite3VdbeAddOp(v, OP_IfNot, jumpIfNull, dest); - break; - } - } -} - -/* -** Do a deep comparison of two expression trees. Return TRUE (non-zero) -** if they are identical and return FALSE if they differ in any way. -*/ -int sqlite3ExprCompare(Expr *pA, Expr *pB){ - int i; - if( pA==0 ){ - return pB==0; - }else if( pB==0 ){ - return 0; - } - if( pA->op!=pB->op ) return 0; - if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0; - if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0; - if( pA->pList ){ - if( pB->pList==0 ) return 0; - if( pA->pList->nExpr!=pB->pList->nExpr ) return 0; - for(i=0; i<pA->pList->nExpr; i++){ - if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ - return 0; - } - } - }else if( pB->pList ){ - return 0; - } - if( pA->pSelect || pB->pSelect ) return 0; - if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0; - if( pA->token.z ){ - if( pB->token.z==0 ) return 0; - if( pB->token.n!=pA->token.n ) return 0; - if( sqlite3StrNICmp(pA->token.z, pB->token.z, pB->token.n)!=0 ) return 0; - } - return 1; -} - -/* -** Add a new element to the pParse->aAgg[] array and return its index. -*/ -static int appendAggInfo(Parse *pParse){ - if( (pParse->nAgg & 0x7)==0 ){ - int amt = pParse->nAgg + 8; - AggExpr *aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0])); - if( aAgg==0 ){ - return -1; - } - pParse->aAgg = aAgg; - } - memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0])); - return pParse->nAgg++; -} - -/* -** Analyze the given expression looking for aggregate functions and -** for variables that need to be added to the pParse->aAgg[] array. -** Make additional entries to the pParse->aAgg[] array as necessary. -** -** This routine should only be called after the expression has been -** analyzed by sqlite3ExprResolveIds() and sqlite3ExprCheck(). -** -** If errors are seen, leave an error message in zErrMsg and return -** the number of errors. -*/ -int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ - int i; - AggExpr *aAgg; - int nErr = 0; - - if( pExpr==0 ) return 0; - switch( pExpr->op ){ - case TK_COLUMN: { - aAgg = pParse->aAgg; - for(i=0; i<pParse->nAgg; i++){ - if( aAgg[i].isAgg ) continue; - if( aAgg[i].pExpr->iTable==pExpr->iTable - && aAgg[i].pExpr->iColumn==pExpr->iColumn ){ - break; - } - } - if( i>=pParse->nAgg ){ - i = appendAggInfo(pParse); - if( i<0 ) return 1; - pParse->aAgg[i].isAgg = 0; - pParse->aAgg[i].pExpr = pExpr; - } - pExpr->iAgg = i; - break; - } - case TK_AGG_FUNCTION: { - aAgg = pParse->aAgg; - for(i=0; i<pParse->nAgg; i++){ - if( !aAgg[i].isAgg ) continue; - if( sqlite3ExprCompare(aAgg[i].pExpr, pExpr) ){ - break; - } - } - if( i>=pParse->nAgg ){ - u8 enc = pParse->db->enc; - i = appendAggInfo(pParse); - if( i<0 ) return 1; - pParse->aAgg[i].isAgg = 1; - pParse->aAgg[i].pExpr = pExpr; - pParse->aAgg[i].pFunc = sqlite3FindFunction(pParse->db, - pExpr->token.z, pExpr->token.n, - pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0); - } - pExpr->iAgg = i; - break; - } - default: { - if( pExpr->pLeft ){ - nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pLeft); - } - if( nErr==0 && pExpr->pRight ){ - nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pRight); - } - if( nErr==0 && pExpr->pList ){ - int n = pExpr->pList->nExpr; - int i; - for(i=0; nErr==0 && i<n; i++){ - nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pList->a[i].pExpr); - } - } - break; - } - } - return nErr; -} - -/* -** Locate a user function given a name, a number of arguments and a flag -** indicating whether the function prefers UTF-16 over UTF-8. Return a -** pointer to the FuncDef structure that defines that function, or return -** NULL if the function does not exist. -** -** If the createFlag argument is true, then a new (blank) FuncDef -** structure is created and liked into the "db" structure if a -** no matching function previously existed. When createFlag is true -** and the nArg parameter is -1, then only a function that accepts -** any number of arguments will be returned. -** -** If createFlag is false and nArg is -1, then the first valid -** function found is returned. A function is valid if either xFunc -** or xStep is non-zero. -** -** If createFlag is false, then a function with the required name and -** number of arguments may be returned even if the eTextRep flag does not -** match that requested. -*/ -FuncDef *sqlite3FindFunction( - sqlite3 *db, /* An open database */ - const char *zName, /* Name of the function. Not null-terminated */ - int nName, /* Number of characters in the name */ - int nArg, /* Number of arguments. -1 means any number */ - u8 enc, /* Preferred text encoding */ - int createFlag /* Create new entry if true and does not otherwise exist */ -){ - FuncDef *p; /* Iterator variable */ - FuncDef *pFirst; /* First function with this name */ - FuncDef *pBest = 0; /* Best match found so far */ - int bestmatch = 0; - - - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - if( nArg<-1 ) nArg = -1; - - pFirst = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName); - for(p=pFirst; p; p=p->pNext){ - /* During the search for the best function definition, bestmatch is set - ** as follows to indicate the quality of the match with the definition - ** pointed to by pBest: - ** - ** 0: pBest is NULL. No match has been found. - ** 1: A variable arguments function that prefers UTF-8 when a UTF-16 - ** encoding is requested, or vice versa. - ** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is - ** requested, or vice versa. - ** 3: A variable arguments function using the same text encoding. - ** 4: A function with the exact number of arguments requested that - ** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa. - ** 5: A function with the exact number of arguments requested that - ** prefers UTF-16LE when UTF-16BE is requested, or vice versa. - ** 6: An exact match. - ** - ** A larger value of 'matchqual' indicates a more desirable match. - */ - if( p->nArg==-1 || p->nArg==nArg || nArg==-1 ){ - int match = 1; /* Quality of this match */ - if( p->nArg==nArg || nArg==-1 ){ - match = 4; - } - if( enc==p->iPrefEnc ){ - match += 2; - } - else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) || - (enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){ - match += 1; - } - - if( match>bestmatch ){ - pBest = p; - bestmatch = match; - } - } - } - - /* If the createFlag parameter is true, and the seach did not reveal an - ** exact match for the name, number of arguments and encoding, then add a - ** new entry to the hash table and return it. - */ - if( createFlag && bestmatch<6 && - (pBest = sqliteMalloc(sizeof(*pBest)+nName+1)) ){ - pBest->nArg = nArg; - pBest->pNext = pFirst; - pBest->zName = (char*)&pBest[1]; - pBest->iPrefEnc = enc; - memcpy(pBest->zName, zName, nName); - pBest->zName[nName] = 0; - sqlite3HashInsert(&db->aFunc, pBest->zName, nName, (void*)pBest); - } - - if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){ - return pBest; - } - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/func.c b/kopete/plugins/statistics/sqlite/func.c deleted file mode 100644 index f61bdae3..00000000 --- a/kopete/plugins/statistics/sqlite/func.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* -** 2002 February 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement various SQL -** functions of SQLite. -** -** There is only one exported symbol in this file - the function -** sqliteRegisterBuildinFunctions() found at the bottom of the file. -** All other code has file scope. -** -** $Id$ -*/ -#include <ctype.h> -#include <math.h> -#include <stdlib.h> -#include <assert.h> -#include "sqliteInt.h" -#include "vdbeInt.h" -#include "os.h" - -static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){ - return context->pColl; -} - -/* -** Implementation of the non-aggregate min() and max() functions -*/ -static void minmaxFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int i; - int mask; /* 0 for min() or 0xffffffff for max() */ - int iBest; - CollSeq *pColl; - - if( argc==0 ) return; - mask = sqlite3_user_data(context)==0 ? 0 : -1; - pColl = sqlite3GetFuncCollSeq(context); - assert( pColl ); - assert( mask==-1 || mask==0 ); - iBest = 0; - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - for(i=1; i<argc; i++){ - if( sqlite3_value_type(argv[i])==SQLITE_NULL ) return; - if( (sqlite3MemCompare(argv[iBest], argv[i], pColl)^mask)>=0 ){ - iBest = i; - } - } - sqlite3_result_value(context, argv[iBest]); -} - -/* -** Return the type of the argument. -*/ -static void typeofFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *z = 0; - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_NULL: z = "null"; break; - case SQLITE_INTEGER: z = "integer"; break; - case SQLITE_TEXT: z = "text"; break; - case SQLITE_FLOAT: z = "real"; break; - case SQLITE_BLOB: z = "blob"; break; - } - sqlite3_result_text(context, z, -1, SQLITE_STATIC); -} - -/* -** Implementation of the length() function -*/ -static void lengthFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int len; - - assert( argc==1 ); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_BLOB: - case SQLITE_INTEGER: - case SQLITE_FLOAT: { - sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); - break; - } - case SQLITE_TEXT: { - const char *z = sqlite3_value_text(argv[0]); - for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; } - sqlite3_result_int(context, len); - break; - } - default: { - sqlite3_result_null(context); - break; - } - } -} - -/* -** Implementation of the abs() function -*/ -static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - assert( argc==1 ); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_INTEGER: { - i64 iVal = sqlite3_value_int64(argv[0]); - if( iVal<0 ) iVal = iVal * -1; - sqlite3_result_int64(context, iVal); - break; - } - case SQLITE_NULL: { - sqlite3_result_null(context); - break; - } - default: { - double rVal = sqlite3_value_double(argv[0]); - if( rVal<0 ) rVal = rVal * -1.0; - sqlite3_result_double(context, rVal); - break; - } - } -} - -/* -** Implementation of the substr() function -*/ -static void substrFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *z; - const char *z2; - int i; - int p1, p2, len; - - assert( argc==3 ); - z = sqlite3_value_text(argv[0]); - if( z==0 ) return; - p1 = sqlite3_value_int(argv[1]); - p2 = sqlite3_value_int(argv[2]); - for(len=0, z2=z; *z2; z2++){ if( (0xc0&*z2)!=0x80 ) len++; } - if( p1<0 ){ - p1 += len; - if( p1<0 ){ - p2 += p1; - p1 = 0; - } - }else if( p1>0 ){ - p1--; - } - if( p1+p2>len ){ - p2 = len-p1; - } - for(i=0; i<p1 && z[i]; i++){ - if( (z[i]&0xc0)==0x80 ) p1++; - } - while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p1++; } - for(; i<p1+p2 && z[i]; i++){ - if( (z[i]&0xc0)==0x80 ) p2++; - } - while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p2++; } - if( p2<0 ) p2 = 0; - sqlite3_result_text(context, &z[p1], p2, SQLITE_TRANSIENT); -} - -/* -** Implementation of the round() function -*/ -static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - int n = 0; - double r; - char zBuf[100]; - assert( argc==1 || argc==2 ); - if( argc==2 ){ - if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; - n = sqlite3_value_int(argv[1]); - if( n>30 ) n = 30; - if( n<0 ) n = 0; - } - if( SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; - r = sqlite3_value_double(argv[0]); - sprintf(zBuf,"%.*f",n,r); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -} - -/* -** Implementation of the upper() and lower() SQL functions. -*/ -static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - unsigned char *z; - int i; - if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; - z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1); - if( z==0 ) return; - strcpy(z, sqlite3_value_text(argv[0])); - for(i=0; z[i]; i++){ - z[i] = toupper(z[i]); - } - sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); - sqliteFree(z); -} -static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - unsigned char *z; - int i; - if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; - z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1); - if( z==0 ) return; - strcpy(z, sqlite3_value_text(argv[0])); - for(i=0; z[i]; i++){ - z[i] = tolower(z[i]); - } - sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); - sqliteFree(z); -} - -/* -** Implementation of the IFNULL(), NVL(), and COALESCE() functions. -** All three do the same thing. They return the first non-NULL -** argument. -*/ -static void ifnullFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int i; - for(i=0; i<argc; i++){ - if( SQLITE_NULL!=sqlite3_value_type(argv[i]) ){ - sqlite3_result_value(context, argv[i]); - break; - } - } -} - -/* -** Implementation of random(). Return a random integer. -*/ -static void randomFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int r; - sqlite3Randomness(sizeof(r), &r); - sqlite3_result_int(context, r); -} - -/* -** Implementation of the last_insert_rowid() SQL function. The return -** value is the same as the sqlite3_last_insert_rowid() API function. -*/ -static void last_insert_rowid( - sqlite3_context *context, - int arg, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_user_data(context); - sqlite3_result_int64(context, sqlite3_last_insert_rowid(db)); -} - -/* -** Implementation of the changes() SQL function. The return value is the -** same as the sqlite3_changes() API function. -*/ -static void changes( - sqlite3_context *context, - int arg, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_user_data(context); - sqlite3_result_int(context, sqlite3_changes(db)); -} - -/* -** Implementation of the total_changes() SQL function. The return value is -** the same as the sqlite3_total_changes() API function. -*/ -static void total_changes( - sqlite3_context *context, - int arg, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_user_data(context); - sqlite3_result_int(context, sqlite3_total_changes(db)); -} - -/* -** A structure defining how to do GLOB-style comparisons. -*/ -struct compareInfo { - u8 matchAll; - u8 matchOne; - u8 matchSet; - u8 noCase; -}; -static const struct compareInfo globInfo = { '*', '?', '[', 0 }; -static const struct compareInfo likeInfo = { '%', '_', 0, 1 }; - -/* -** X is a pointer to the first byte of a UTF-8 character. Increment -** X so that it points to the next character. This only works right -** if X points to a well-formed UTF-8 string. -*/ -#define sqliteNextChar(X) while( (0xc0&*++(X))==0x80 ){} -#define sqliteCharVal(X) sqlite3ReadUtf8(X) - - -/* -** Compare two UTF-8 strings for equality where the first string can -** potentially be a "glob" expression. Return true (1) if they -** are the same and false (0) if they are different. -** -** Globbing rules: -** -** '*' Matches any sequence of zero or more characters. -** -** '?' Matches exactly one character. -** -** [...] Matches one character from the enclosed list of -** characters. -** -** [^...] Matches one character not in the enclosed list. -** -** With the [...] and [^...] matching, a ']' character can be included -** in the list by making it the first character after '[' or '^'. A -** range of characters can be specified using '-'. Example: -** "[a-z]" matches any single lower-case letter. To match a '-', make -** it the last character in the list. -** -** This routine is usually quick, but can be N**2 in the worst case. -** -** Hints: to match '*' or '?', put them in "[]". Like this: -** -** abc[*]xyz Matches "abc*xyz" only -*/ -int patternCompare( - const u8 *zPattern, /* The glob pattern */ - const u8 *zString, /* The string to compare against the glob */ - const struct compareInfo *pInfo /* Information about how to do the compare */ -){ - register int c; - int invert; - int seen; - int c2; - u8 matchOne = pInfo->matchOne; - u8 matchAll = pInfo->matchAll; - u8 matchSet = pInfo->matchSet; - u8 noCase = pInfo->noCase; - - while( (c = *zPattern)!=0 ){ - if( c==matchAll ){ - while( (c=zPattern[1]) == matchAll || c == matchOne ){ - if( c==matchOne ){ - if( *zString==0 ) return 0; - sqliteNextChar(zString); - } - zPattern++; - } - if( c==0 ) return 1; - if( c==matchSet ){ - while( *zString && patternCompare(&zPattern[1],zString,pInfo)==0 ){ - sqliteNextChar(zString); - } - return *zString!=0; - }else{ - while( (c2 = *zString)!=0 ){ - if( noCase ){ - c2 = sqlite3UpperToLower[c2]; - c = sqlite3UpperToLower[c]; - while( c2 != 0 && c2 != c ){ c2 = sqlite3UpperToLower[*++zString]; } - }else{ - while( c2 != 0 && c2 != c ){ c2 = *++zString; } - } - if( c2==0 ) return 0; - if( patternCompare(&zPattern[1],zString,pInfo) ) return 1; - sqliteNextChar(zString); - } - return 0; - } - }else if( c==matchOne ){ - if( *zString==0 ) return 0; - sqliteNextChar(zString); - zPattern++; - }else if( c==matchSet ){ - int prior_c = 0; - seen = 0; - invert = 0; - c = sqliteCharVal(zString); - if( c==0 ) return 0; - c2 = *++zPattern; - if( c2=='^' ){ invert = 1; c2 = *++zPattern; } - if( c2==']' ){ - if( c==']' ) seen = 1; - c2 = *++zPattern; - } - while( (c2 = sqliteCharVal(zPattern))!=0 && c2!=']' ){ - if( c2=='-' && zPattern[1]!=']' && zPattern[1]!=0 && prior_c>0 ){ - zPattern++; - c2 = sqliteCharVal(zPattern); - if( c>=prior_c && c<=c2 ) seen = 1; - prior_c = 0; - }else if( c==c2 ){ - seen = 1; - prior_c = c2; - }else{ - prior_c = c2; - } - sqliteNextChar(zPattern); - } - if( c2==0 || (seen ^ invert)==0 ) return 0; - sqliteNextChar(zString); - zPattern++; - }else{ - if( noCase ){ - if( sqlite3UpperToLower[c] != sqlite3UpperToLower[*zString] ) return 0; - }else{ - if( c != *zString ) return 0; - } - zPattern++; - zString++; - } - } - return *zString==0; -} - - -/* -** Implementation of the like() SQL function. This function implements -** the build-in LIKE operator. The first argument to the function is the -** pattern and the second argument is the string. So, the SQL statements: -** -** A LIKE B -** -** is implemented as like(B,A). -** -** If the pointer retrieved by via a call to sqlite3_user_data() is -** not NULL, then this function uses UTF-16. Otherwise UTF-8. -*/ -static void likeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zA = sqlite3_value_text(argv[0]); - const unsigned char *zB = sqlite3_value_text(argv[1]); - if( zA && zB ){ - sqlite3_result_int(context, patternCompare(zA, zB, &likeInfo)); - } -} - -/* -** Implementation of the glob() SQL function. This function implements -** the build-in GLOB operator. The first argument to the function is the -** string and the second argument is the pattern. So, the SQL statements: -** -** A GLOB B -** -** is implemented as glob(A,B). -*/ -static void globFunc(sqlite3_context *context, int arg, sqlite3_value **argv){ - const unsigned char *zA = sqlite3_value_text(argv[0]); - const unsigned char *zB = sqlite3_value_text(argv[1]); - if( zA && zB ){ - sqlite3_result_int(context, patternCompare(zA, zB, &globInfo)); - } -} - -/* -** Implementation of the NULLIF(x,y) function. The result is the first -** argument if the arguments are different. The result is NULL if the -** arguments are equal to each other. -*/ -static void nullifFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - CollSeq *pColl = sqlite3GetFuncCollSeq(context); - if( sqlite3MemCompare(argv[0], argv[1], pColl)!=0 ){ - sqlite3_result_value(context, argv[0]); - } -} - -/* -** Implementation of the VERSION(*) function. The result is the version -** of the SQLite library that is running. -*/ -static void versionFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); -} - -/* -** EXPERIMENTAL - This is not an official function. The interface may -** change. This function may disappear. Do not write code that depends -** on this function. -** -** Implementation of the QUOTE() function. This function takes a single -** argument. If the argument is numeric, the return value is the same as -** the argument. If the argument is NULL, the return value is the string -** "NULL". Otherwise, the argument is enclosed in single quotes with -** single-quote escapes. -*/ -static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - if( argc<1 ) return; - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_NULL: { - sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); - break; - } - case SQLITE_INTEGER: - case SQLITE_FLOAT: { - sqlite3_result_value(context, argv[0]); - break; - } - case SQLITE_BLOB: { - static const char hexdigits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - char *zText = 0; - int nBlob = sqlite3_value_bytes(argv[0]); - char const *zBlob = sqlite3_value_blob(argv[0]); - - zText = (char *)sqliteMalloc((2*nBlob)+4); - if( !zText ){ - sqlite3_result_error(context, "out of memory", -1); - }else{ - int i; - for(i=0; i<nBlob; i++){ - zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F]; - zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F]; - } - zText[(nBlob*2)+2] = '\''; - zText[(nBlob*2)+3] = '\0'; - zText[0] = 'X'; - zText[1] = '\''; - sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); - sqliteFree(zText); - } - break; - } - case SQLITE_TEXT: { - int i,j,n; - const char *zArg = sqlite3_value_text(argv[0]); - char *z; - - for(i=n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } - z = sqliteMalloc( i+n+3 ); - if( z==0 ) return; - z[0] = '\''; - for(i=0, j=1; zArg[i]; i++){ - z[j++] = zArg[i]; - if( zArg[i]=='\'' ){ - z[j++] = '\''; - } - } - z[j++] = '\''; - z[j] = 0; - sqlite3_result_text(context, z, j, SQLITE_TRANSIENT); - sqliteFree(z); - } - } -} - -#ifdef SQLITE_SOUNDEX -/* -** Compute the soundex encoding of a word. -*/ -static void soundexFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - char zResult[8]; - const u8 *zIn; - int i, j; - static const unsigned char iCode[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - }; - assert( argc==1 ); - zIn = (u8*)sqlite3_value_text(argv[0]); - for(i=0; zIn[i] && !isalpha(zIn[i]); i++){} - if( zIn[i] ){ - zResult[0] = toupper(zIn[i]); - for(j=1; j<4 && zIn[i]; i++){ - int code = iCode[zIn[i]&0x7f]; - if( code>0 ){ - zResult[j++] = code + '0'; - } - } - while( j<4 ){ - zResult[j++] = '0'; - } - zResult[j] = 0; - sqlite3_result_text(context, zResult, 4, SQLITE_TRANSIENT); - }else{ - sqlite3_result_text(context, "?000", 4, SQLITE_STATIC); - } -} -#endif - -#ifdef SQLITE_TEST -/* -** This function generates a string of random characters. Used for -** generating test data. -*/ -static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){ - static const unsigned char zSrc[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - ".-!,:*^+=_|?/<> "; - int iMin, iMax, n, r, i; - unsigned char zBuf[1000]; - if( argc>=1 ){ - iMin = sqlite3_value_int(argv[0]); - if( iMin<0 ) iMin = 0; - if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1; - }else{ - iMin = 1; - } - if( argc>=2 ){ - iMax = sqlite3_value_int(argv[1]); - if( iMax<iMin ) iMax = iMin; - if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf)-1; - }else{ - iMax = 50; - } - n = iMin; - if( iMax>iMin ){ - sqlite3Randomness(sizeof(r), &r); - r &= 0x7fffffff; - n += r%(iMax + 1 - iMin); - } - assert( n<sizeof(zBuf) ); - sqlite3Randomness(n, zBuf); - for(i=0; i<n; i++){ - zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)]; - } - zBuf[n] = 0; - sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); -} -#endif /* SQLITE_TEST */ - -#ifdef SQLITE_TEST -/* -** The following two SQL functions are used to test returning a text -** result with a destructor. Function 'test_destructor' takes one argument -** and returns the same argument interpreted as TEXT. A destructor is -** passed with the sqlite3_result_text() call. -** -** SQL function 'test_destructor_count' returns the number of outstanding -** allocations made by 'test_destructor'; -** -** WARNING: Not threadsafe. -*/ -static int test_destructor_count_var = 0; -static void destructor(void *p){ - char *zVal = (char *)p; - assert(zVal); - zVal--; - sqliteFree(zVal); - test_destructor_count_var--; -} -static void test_destructor( - sqlite3_context *pCtx, - int nArg, - sqlite3_value **argv -){ - char *zVal; - int len; - sqlite3 *db = sqlite3_user_data(pCtx); - - test_destructor_count_var++; - assert( nArg==1 ); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - len = sqlite3ValueBytes(argv[0], db->enc); - zVal = sqliteMalloc(len+3); - zVal[len] = 0; - zVal[len-1] = 0; - assert( zVal ); - zVal++; - memcpy(zVal, sqlite3ValueText(argv[0], db->enc), len); - if( db->enc==SQLITE_UTF8 ){ - sqlite3_result_text(pCtx, zVal, -1, destructor); - }else if( db->enc==SQLITE_UTF16LE ){ - sqlite3_result_text16le(pCtx, zVal, -1, destructor); - }else{ - sqlite3_result_text16be(pCtx, zVal, -1, destructor); - } -} -static void test_destructor_count( - sqlite3_context *pCtx, - int nArg, - sqlite3_value **argv -){ - sqlite3_result_int(pCtx, test_destructor_count_var); -} -#endif /* SQLITE_TEST */ - -#ifdef SQLITE_TEST -/* -** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata() -** interface. -** -** The test_auxdata() SQL function attempts to register each of its arguments -** as auxiliary data. If there are no prior registrations of aux data for -** that argument (meaning the argument is not a constant or this is its first -** call) then the result for that argument is 0. If there is a prior -** registration, the result for that argument is 1. The overall result -** is the individual argument results separated by spaces. -*/ -static void free_test_auxdata(void *p) {sqliteFree(p);} -static void test_auxdata( - sqlite3_context *pCtx, - int nArg, - sqlite3_value **argv -){ - int i; - char *zRet = sqliteMalloc(nArg*2); - if( !zRet ) return; - for(i=0; i<nArg; i++){ - char const *z = sqlite3_value_text(argv[i]); - if( z ){ - char *zAux = sqlite3_get_auxdata(pCtx, i); - if( zAux ){ - zRet[i*2] = '1'; - if( strcmp(zAux, z) ){ - sqlite3_result_error(pCtx, "Auxilary data corruption", -1); - return; - } - }else{ - zRet[i*2] = '0'; - zAux = sqliteStrDup(z); - sqlite3_set_auxdata(pCtx, i, zAux, free_test_auxdata); - } - zRet[i*2+1] = ' '; - } - } - sqlite3_result_text(pCtx, zRet, 2*nArg-1, free_test_auxdata); -} -#endif /* SQLITE_TEST */ - -/* -** An instance of the following structure holds the context of a -** sum() or avg() aggregate computation. -*/ -typedef struct SumCtx SumCtx; -struct SumCtx { - double sum; /* Sum of terms */ - int cnt; /* Number of elements summed */ -}; - -/* -** Routines used to compute the sum or average. -*/ -static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - SumCtx *p; - if( argc<1 ) return; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p && SQLITE_NULL!=sqlite3_value_type(argv[0]) ){ - p->sum += sqlite3_value_double(argv[0]); - p->cnt++; - } -} -static void sumFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, sizeof(*p)); - sqlite3_result_double(context, p ? p->sum : 0.0); -} -static void avgFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p && p->cnt>0 ){ - sqlite3_result_double(context, p->sum/(double)p->cnt); - } -} - -/* -** An instance of the following structure holds the context of a -** variance or standard deviation computation. -*/ -typedef struct StdDevCtx StdDevCtx; -struct StdDevCtx { - double sum; /* Sum of terms */ - double sum2; /* Sum of the squares of terms */ - int cnt; /* Number of terms counted */ -}; - -#if 0 /* Omit because math library is required */ -/* -** Routines used to compute the standard deviation as an aggregate. -*/ -static void stdDevStep(sqlite3_context *context, int argc, const char **argv){ - StdDevCtx *p; - double x; - if( argc<1 ) return; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p && argv[0] ){ - x = sqlite3AtoF(argv[0], 0); - p->sum += x; - p->sum2 += x*x; - p->cnt++; - } -} -static void stdDevFinalize(sqlite3_context *context){ - double rN = sqlite3_aggregate_count(context); - StdDevCtx *p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p && p->cnt>1 ){ - double rCnt = cnt; - sqlite3_set_result_double(context, - sqrt((p->sum2 - p->sum*p->sum/rCnt)/(rCnt-1.0))); - } -} -#endif - -/* -** The following structure keeps track of state information for the -** count() aggregate function. -*/ -typedef struct CountCtx CountCtx; -struct CountCtx { - int n; -}; - -/* -** Routines to implement the count() aggregate function. -*/ -static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - CountCtx *p; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){ - p->n++; - } -} -static void countFinalize(sqlite3_context *context){ - CountCtx *p; - p = sqlite3_aggregate_context(context, sizeof(*p)); - sqlite3_result_int(context, p ? p->n : 0); -} - -/* -** This function tracks state information for the min() and max() -** aggregate functions. -*/ -typedef struct MinMaxCtx MinMaxCtx; -struct MinMaxCtx { - char *z; /* The best so far */ - char zBuf[28]; /* Space that can be used for storage */ -}; - -/* -** Routines to implement min() and max() aggregate functions. -*/ -static void minmaxStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - Mem *pArg = (Mem *)argv[0]; - Mem *pBest; - - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest)); - if( !pBest ) return; - - if( pBest->flags ){ - int max; - int cmp; - CollSeq *pColl = sqlite3GetFuncCollSeq(context); - /* This step function is used for both the min() and max() aggregates, - ** the only difference between the two being that the sense of the - ** comparison is inverted. For the max() aggregate, the - ** sqlite3_user_data() function returns (void *)-1. For min() it - ** returns (void *)db, where db is the sqlite3* database pointer. - ** Therefore the next statement sets variable 'max' to 1 for the max() - ** aggregate, or 0 for min(). - */ - max = ((sqlite3_user_data(context)==(void *)-1)?1:0); - cmp = sqlite3MemCompare(pBest, pArg, pColl); - if( (max && cmp<0) || (!max && cmp>0) ){ - sqlite3VdbeMemCopy(pBest, pArg); - } - }else{ - sqlite3VdbeMemCopy(pBest, pArg); - } -} -static void minMaxFinalize(sqlite3_context *context){ - sqlite3_value *pRes; - pRes = (sqlite3_value *)sqlite3_aggregate_context(context, sizeof(Mem)); - if( pRes->flags ){ - sqlite3_result_value(context, pRes); - } - sqlite3VdbeMemRelease(pRes); -} - - -/* -** This function registered all of the above C functions as SQL -** functions. This should be the only routine in this file with -** external linkage. -*/ -void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ - static const struct { - char *zName; - signed char nArg; - u8 argType; /* 0: none. 1: db 2: (-1) */ - u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */ - u8 needCollSeq; - void (*xFunc)(sqlite3_context*,int,sqlite3_value **); - } aFuncs[] = { - { "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc }, - { "min", 0, 0, SQLITE_UTF8, 1, 0 }, - { "max", -1, 2, SQLITE_UTF8, 1, minmaxFunc }, - { "max", 0, 2, SQLITE_UTF8, 1, 0 }, - { "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc }, - { "length", 1, 0, SQLITE_UTF8, 0, lengthFunc }, - { "substr", 3, 0, SQLITE_UTF8, 0, substrFunc }, - { "substr", 3, 0, SQLITE_UTF16LE, 0, sqlite3utf16Substr }, - { "abs", 1, 0, SQLITE_UTF8, 0, absFunc }, - { "round", 1, 0, SQLITE_UTF8, 0, roundFunc }, - { "round", 2, 0, SQLITE_UTF8, 0, roundFunc }, - { "upper", 1, 0, SQLITE_UTF8, 0, upperFunc }, - { "lower", 1, 0, SQLITE_UTF8, 0, lowerFunc }, - { "coalesce", -1, 0, SQLITE_UTF8, 0, ifnullFunc }, - { "coalesce", 0, 0, SQLITE_UTF8, 0, 0 }, - { "coalesce", 1, 0, SQLITE_UTF8, 0, 0 }, - { "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc }, - { "random", -1, 0, SQLITE_UTF8, 0, randomFunc }, - { "like", 2, 0, SQLITE_UTF8, 0, likeFunc }, - { "glob", 2, 0, SQLITE_UTF8, 0, globFunc }, - { "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc }, - { "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc}, - { "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc }, - { "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid }, - { "changes", 0, 1, SQLITE_UTF8, 0, changes }, - { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, -#ifdef SQLITE_SOUNDEX - { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, -#endif -#ifdef SQLITE_TEST - { "randstr", 2, 0, SQLITE_UTF8, 0, randStr }, - { "test_destructor", 1, 1, SQLITE_UTF8, 0, test_destructor}, - { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count}, - { "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata}, -#endif - }; - static const struct { - char *zName; - signed char nArg; - u8 argType; - u8 needCollSeq; - void (*xStep)(sqlite3_context*,int,sqlite3_value**); - void (*xFinalize)(sqlite3_context*); - } aAggs[] = { - { "min", 1, 0, 1, minmaxStep, minMaxFinalize }, - { "max", 1, 2, 1, minmaxStep, minMaxFinalize }, - { "sum", 1, 0, 0, sumStep, sumFinalize }, - { "avg", 1, 0, 0, sumStep, avgFinalize }, - { "count", 0, 0, 0, countStep, countFinalize }, - { "count", 1, 0, 0, countStep, countFinalize }, -#if 0 - { "stddev", 1, 0, stdDevStep, stdDevFinalize }, -#endif - }; - int i; - - for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ - void *pArg = 0; - switch( aFuncs[i].argType ){ - case 1: pArg = db; break; - case 2: pArg = (void *)(-1); break; - } - sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, - aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0); - if( aFuncs[i].needCollSeq ){ - FuncDef *pFunc = sqlite3FindFunction(db, aFuncs[i].zName, - strlen(aFuncs[i].zName), aFuncs[i].nArg, aFuncs[i].eTextRep, 0); - if( pFunc && aFuncs[i].needCollSeq ){ - pFunc->needCollSeq = 1; - } - } - } - for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){ - void *pArg = 0; - switch( aAggs[i].argType ){ - case 1: pArg = db; break; - case 2: pArg = (void *)(-1); break; - } - sqlite3_create_function(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8, - pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize); - if( aAggs[i].needCollSeq ){ - FuncDef *pFunc = sqlite3FindFunction( db, aAggs[i].zName, - strlen(aAggs[i].zName), aAggs[i].nArg, SQLITE_UTF8, 0); - if( pFunc && aAggs[i].needCollSeq ){ - pFunc->needCollSeq = 1; - } - } - } - sqlite3RegisterDateTimeFunctions(db); -} diff --git a/kopete/plugins/statistics/sqlite/hash.c b/kopete/plugins/statistics/sqlite/hash.c deleted file mode 100644 index 23e2e197..00000000 --- a/kopete/plugins/statistics/sqlite/hash.c +++ /dev/null @@ -1,380 +0,0 @@ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the implementation of generic hash-tables -** used in SQLite. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include <assert.h> - -/* Turn bulk memory into a hash table object by initializing the -** fields of the Hash structure. -** -** "pNew" is a pointer to the hash table that is to be initialized. -** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER, -** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass -** determines what kind of key the hash table will use. "copyKey" is -** true if the hash table should make its own private copy of keys and -** false if it should just use the supplied pointer. CopyKey only makes -** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored -** for other key classes. -*/ -void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){ - assert( pNew!=0 ); - assert( keyClass>=SQLITE_HASH_STRING && keyClass<=SQLITE_HASH_BINARY ); - pNew->keyClass = keyClass; -#if 0 - if( keyClass==SQLITE_HASH_POINTER || keyClass==SQLITE_HASH_INT ) copyKey = 0; -#endif - pNew->copyKey = copyKey; - pNew->first = 0; - pNew->count = 0; - pNew->htsize = 0; - pNew->ht = 0; -} - -/* Remove all entries from a hash table. Reclaim all memory. -** Call this routine to delete a hash table or to reset a hash table -** to the empty state. -*/ -void sqlite3HashClear(Hash *pH){ - HashElem *elem; /* For looping over all elements of the table */ - - assert( pH!=0 ); - elem = pH->first; - pH->first = 0; - if( pH->ht ) sqliteFree(pH->ht); - pH->ht = 0; - pH->htsize = 0; - while( elem ){ - HashElem *next_elem = elem->next; - if( pH->copyKey && elem->pKey ){ - sqliteFree(elem->pKey); - } - sqliteFree(elem); - elem = next_elem; - } - pH->count = 0; -} - -#if 0 /* NOT USED */ -/* -** Hash and comparison functions when the mode is SQLITE_HASH_INT -*/ -static int intHash(const void *pKey, int nKey){ - return nKey ^ (nKey<<8) ^ (nKey>>8); -} -static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - return n2 - n1; -} -#endif - -#if 0 /* NOT USED */ -/* -** Hash and comparison functions when the mode is SQLITE_HASH_POINTER -*/ -static int ptrHash(const void *pKey, int nKey){ - uptr x = Addr(pKey); - return x ^ (x<<8) ^ (x>>8); -} -static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( pKey1==pKey2 ) return 0; - if( pKey1<pKey2 ) return -1; - return 1; -} -#endif - -/* -** Hash and comparison functions when the mode is SQLITE_HASH_STRING -*/ -static int strHash(const void *pKey, int nKey){ - return sqlite3HashNoCase((const char*)pKey, nKey); -} -static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1); -} - -/* -** Hash and comparison functions when the mode is SQLITE_HASH_BINARY -*/ -static int binHash(const void *pKey, int nKey){ - int h = 0; - const char *z = (const char *)pKey; - while( nKey-- > 0 ){ - h = (h<<3) ^ h ^ *(z++); - } - return h & 0x7fffffff; -} -static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return memcmp(pKey1,pKey2,n1); -} - -/* -** Return a pointer to the appropriate hash function given the key class. -** -** The C syntax in this function definition may be unfamilar to some -** programmers, so we provide the following additional explanation: -** -** The name of the function is "hashFunction". The function takes a -** single parameter "keyClass". The return value of hashFunction() -** is a pointer to another function. Specifically, the return value -** of hashFunction() is a pointer to a function that takes two parameters -** with types "const void*" and "int" and returns an "int". -*/ -static int (*hashFunction(int keyClass))(const void*,int){ -#if 0 /* HASH_INT and HASH_POINTER are never used */ - switch( keyClass ){ - case SQLITE_HASH_INT: return &intHash; - case SQLITE_HASH_POINTER: return &ptrHash; - case SQLITE_HASH_STRING: return &strHash; - case SQLITE_HASH_BINARY: return &binHash;; - default: break; - } - return 0; -#else - if( keyClass==SQLITE_HASH_STRING ){ - return &strHash; - }else{ - assert( keyClass==SQLITE_HASH_BINARY ); - return &binHash; - } -#endif -} - -/* -** Return a pointer to the appropriate hash function given the key class. -** -** For help in interpreted the obscure C code in the function definition, -** see the header comment on the previous function. -*/ -static int (*compareFunction(int keyClass))(const void*,int,const void*,int){ -#if 0 /* HASH_INT and HASH_POINTER are never used */ - switch( keyClass ){ - case SQLITE_HASH_INT: return &intCompare; - case SQLITE_HASH_POINTER: return &ptrCompare; - case SQLITE_HASH_STRING: return &strCompare; - case SQLITE_HASH_BINARY: return &binCompare; - default: break; - } - return 0; -#else - if( keyClass==SQLITE_HASH_STRING ){ - return &strCompare; - }else{ - assert( keyClass==SQLITE_HASH_BINARY ); - return &binCompare; - } -#endif -} - -/* Link an element into the hash table -*/ -static void insertElement( - Hash *pH, /* The complete hash table */ - struct _ht *pEntry, /* The entry into which pNew is inserted */ - HashElem *pNew /* The element to be inserted */ -){ - HashElem *pHead; /* First element already in pEntry */ - pHead = pEntry->chain; - if( pHead ){ - pNew->next = pHead; - pNew->prev = pHead->prev; - if( pHead->prev ){ pHead->prev->next = pNew; } - else { pH->first = pNew; } - pHead->prev = pNew; - }else{ - pNew->next = pH->first; - if( pH->first ){ pH->first->prev = pNew; } - pNew->prev = 0; - pH->first = pNew; - } - pEntry->count++; - pEntry->chain = pNew; -} - - -/* Resize the hash table so that it cantains "new_size" buckets. -** "new_size" must be a power of 2. The hash table might fail -** to resize if sqliteMalloc() fails. -*/ -static void rehash(Hash *pH, int new_size){ - struct _ht *new_ht; /* The new hash table */ - HashElem *elem, *next_elem; /* For looping over existing elements */ - int (*xHash)(const void*,int); /* The hash function */ - - assert( (new_size & (new_size-1))==0 ); - new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) ); - if( new_ht==0 ) return; - if( pH->ht ) sqliteFree(pH->ht); - pH->ht = new_ht; - pH->htsize = new_size; - xHash = hashFunction(pH->keyClass); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); - next_elem = elem->next; - insertElement(pH, &new_ht[h], elem); - } -} - -/* This function (for internal use only) locates an element in an -** hash table that matches the given key. The hash for this key has -** already been computed and is passed as the 4th parameter. -*/ -static HashElem *findElementGivenHash( - const Hash *pH, /* The pH to be searched */ - const void *pKey, /* The key we are searching for */ - int nKey, - int h /* The hash for this key. */ -){ - HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ - int (*xCompare)(const void*,int,const void*,int); /* comparison function */ - - if( pH->ht ){ - struct _ht *pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - xCompare = compareFunction(pH->keyClass); - while( count-- && elem ){ - if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; - } - } - return 0; -} - -/* Remove a single entry from the hash table given a pointer to that -** element and a hash on the element's key. -*/ -static void removeElementGivenHash( - Hash *pH, /* The pH containing "elem" */ - HashElem* elem, /* The element to be removed from the pH */ - int h /* Hash value for the element */ -){ - struct _ht *pEntry; - if( elem->prev ){ - elem->prev->next = elem->next; - }else{ - pH->first = elem->next; - } - if( elem->next ){ - elem->next->prev = elem->prev; - } - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; - } - pEntry->count--; - if( pEntry->count<=0 ){ - pEntry->chain = 0; - } - if( pH->copyKey && elem->pKey ){ - sqliteFree(elem->pKey); - } - sqliteFree( elem ); - pH->count--; -} - -/* Attempt to locate an element of the hash table pH with a key -** that matches pKey,nKey. Return the data for this element if it is -** found, or NULL if there is no match. -*/ -void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){ - int h; /* A hash on key */ - HashElem *elem; /* The element that matches key */ - int (*xHash)(const void*,int); /* The hash function */ - - if( pH==0 || pH->ht==0 ) return 0; - xHash = hashFunction(pH->keyClass); - assert( xHash!=0 ); - h = (*xHash)(pKey,nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1)); - return elem ? elem->data : 0; -} - -/* Insert an element into the hash table pH. The key is pKey,nKey -** and the data is "data". -** -** If no element exists with a matching key, then a new -** element is created. A copy of the key is made if the copyKey -** flag is set. NULL is returned. -** -** If another element already exists with the same key, then the -** new data replaces the old data and the old data is returned. -** The key is not copied in this instance. If a malloc fails, then -** the new data is returned and the hash table is unchanged. -** -** If the "data" parameter to this function is NULL, then the -** element corresponding to "key" is removed from the hash table. -*/ -void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){ - int hraw; /* Raw hash value of the key */ - int h; /* the hash of the key modulo hash table size */ - HashElem *elem; /* Used to loop thru the element list */ - HashElem *new_elem; /* New element added to the pH */ - int (*xHash)(const void*,int); /* The hash function */ - - assert( pH!=0 ); - xHash = hashFunction(pH->keyClass); - assert( xHash!=0 ); - hraw = (*xHash)(pKey, nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - elem = findElementGivenHash(pH,pKey,nKey,h); - if( elem ){ - void *old_data = elem->data; - if( data==0 ){ - removeElementGivenHash(pH,elem,h); - }else{ - elem->data = data; - } - return old_data; - } - if( data==0 ) return 0; - new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) ); - if( new_elem==0 ) return data; - if( pH->copyKey && pKey!=0 ){ - new_elem->pKey = sqliteMallocRaw( nKey ); - if( new_elem->pKey==0 ){ - sqliteFree(new_elem); - return data; - } - memcpy((void*)new_elem->pKey, pKey, nKey); - }else{ - new_elem->pKey = (void*)pKey; - } - new_elem->nKey = nKey; - pH->count++; - if( pH->htsize==0 ){ - rehash(pH,8); - if( pH->htsize==0 ){ - pH->count = 0; - sqliteFree(new_elem); - return data; - } - } - if( pH->count > pH->htsize ){ - rehash(pH,pH->htsize*2); - } - assert( pH->htsize>0 ); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - insertElement(pH, &pH->ht[h], new_elem); - new_elem->data = data; - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/hash.h b/kopete/plugins/statistics/sqlite/hash.h deleted file mode 100644 index cf004ddc..00000000 --- a/kopete/plugins/statistics/sqlite/hash.h +++ /dev/null @@ -1,109 +0,0 @@ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the header file for the generic hash-table implemenation -** used in SQLite. -** -** $Id$ -*/ -#ifndef _SQLITE_HASH_H_ -#define _SQLITE_HASH_H_ - -/* Forward declarations of structures. */ -typedef struct Hash Hash; -typedef struct HashElem HashElem; - -/* A complete hash table is an instance of the following structure. -** The internals of this structure are intended to be opaque -- client -** code should not attempt to access or modify the fields of this structure -** directly. Change this structure only by using the routines below. -** However, many of the "procedures" and "functions" for modifying and -** accessing this structure are really macros, so we can't really make -** this structure opaque. -*/ -struct Hash { - char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */ - char copyKey; /* True if copy of key made on insert */ - int count; /* Number of entries in this table */ - HashElem *first; /* The first element of the array */ - int htsize; /* Number of buckets in the hash table */ - struct _ht { /* the hash table */ - int count; /* Number of entries with this hash */ - HashElem *chain; /* Pointer to first entry with this hash */ - } *ht; -}; - -/* Each element in the hash table is an instance of the following -** structure. All elements are stored on a single doubly-linked list. -** -** Again, this structure is intended to be opaque, but it can't really -** be opaque because it is used by macros. -*/ -struct HashElem { - HashElem *next, *prev; /* Next and previous elements in the table */ - void *data; /* Data associated with this element */ - void *pKey; int nKey; /* Key associated with this element */ -}; - -/* -** There are 4 different modes of operation for a hash table: -** -** SQLITE_HASH_INT nKey is used as the key and pKey is ignored. -** -** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored. -** -** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long -** (including the null-terminator, if any). Case -** is ignored in comparisons. -** -** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long. -** memcmp() is used to compare keys. -** -** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY -** if the copyKey parameter to HashInit is 1. -*/ -/* #define SQLITE_HASH_INT 1 // NOT USED */ -/* #define SQLITE_HASH_POINTER 2 // NOT USED */ -#define SQLITE_HASH_STRING 3 -#define SQLITE_HASH_BINARY 4 - -/* -** Access routines. To delete, insert a NULL pointer. -*/ -void sqlite3HashInit(Hash*, int keytype, int copyKey); -void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData); -void *sqlite3HashFind(const Hash*, const void *pKey, int nKey); -void sqlite3HashClear(Hash*); - -/* -** Macros for looping over all elements of a hash table. The idiom is -** like this: -** -** Hash h; -** HashElem *p; -** ... -** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ -** SomeStructure *pData = sqliteHashData(p); -** // do something with pData -** } -*/ -#define sqliteHashFirst(H) ((H)->first) -#define sqliteHashNext(E) ((E)->next) -#define sqliteHashData(E) ((E)->data) -#define sqliteHashKey(E) ((E)->pKey) -#define sqliteHashKeysize(E) ((E)->nKey) - -/* -** Number of entries in a hash table -*/ -#define sqliteHashCount(H) ((H)->count) - -#endif /* _SQLITE_HASH_H_ */ diff --git a/kopete/plugins/statistics/sqlite/insert.c b/kopete/plugins/statistics/sqlite/insert.c deleted file mode 100644 index 65cbdc8f..00000000 --- a/kopete/plugins/statistics/sqlite/insert.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle INSERT statements in SQLite. -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** Set P3 of the most recently inserted opcode to a column affinity -** string for index pIdx. A column affinity string has one character -** for each column in the table, according to the affinity of the column: -** -** Character Column affinity -** ------------------------------ -** 'n' NUMERIC -** 'i' INTEGER -** 't' TEXT -** 'o' NONE -*/ -void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ - if( !pIdx->zColAff ){ - /* The first time a column affinity string for a particular index is - ** required, it is allocated and populated here. It is then stored as - ** a member of the Index structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqliteDeleteIndex() when the Index structure itself is cleaned - ** up. - */ - int n; - Table *pTab = pIdx->pTable; - pIdx->zColAff = (char *)sqliteMalloc(pIdx->nColumn+1); - if( !pIdx->zColAff ){ - return; - } - for(n=0; n<pIdx->nColumn; n++){ - pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; - } - pIdx->zColAff[pIdx->nColumn] = '\0'; - } - - sqlite3VdbeChangeP3(v, -1, pIdx->zColAff, 0); -} - -/* -** Set P3 of the most recently inserted opcode to a column affinity -** string for table pTab. A column affinity string has one character -** for each column indexed by the index, according to the affinity of the -** column: -** -** Character Column affinity -** ------------------------------ -** 'n' NUMERIC -** 'i' INTEGER -** 't' TEXT -** 'o' NONE -*/ -void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ - /* The first time a column affinity string for a particular table - ** is required, it is allocated and populated here. It is then - ** stored as a member of the Table structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqlite3DeleteTable() when the Table structure itself is cleaned up. - */ - if( !pTab->zColAff ){ - char *zColAff; - int i; - - zColAff = (char *)sqliteMalloc(pTab->nCol+1); - if( !zColAff ){ - return; - } - - for(i=0; i<pTab->nCol; i++){ - zColAff[i] = pTab->aCol[i].affinity; - } - zColAff[pTab->nCol] = '\0'; - - pTab->zColAff = zColAff; - } - - sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0); -} - - -/* -** This routine is call to handle SQL of the following forms: -** -** insert into TABLE (IDLIST) values(EXPRLIST) -** insert into TABLE (IDLIST) select -** -** The IDLIST following the table name is always optional. If omitted, -** then a list of all columns for the table is substituted. The IDLIST -** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. -** -** The pList parameter holds EXPRLIST in the first form of the INSERT -** statement above, and pSelect is NULL. For the second form, pList is -** NULL and pSelect is a pointer to the select statement used to generate -** data for the insert. -** -** The code generated follows one of three templates. For a simple -** select with data coming from a VALUES clause, the code executes -** once straight down through. The template looks like this: -** -** open write cursor to <table> and its indices -** puts VALUES clause expressions onto the stack -** write the resulting record into <table> -** cleanup -** -** If the statement is of the form -** -** INSERT INTO <table> SELECT ... -** -** And the SELECT clause does not read from <table> at any time, then -** the generated code follows this template: -** -** goto B -** A: setup for the SELECT -** loop over the tables in the SELECT -** gosub C -** end loop -** cleanup after the SELECT -** goto D -** B: open write cursor to <table> and its indices -** goto A -** C: insert the select result into <table> -** return -** D: cleanup -** -** The third template is used if the insert statement takes its -** values from a SELECT but the data is being inserted into a table -** that is also read as part of the SELECT. In the third form, -** we have to use a intermediate table to store the results of -** the select. The template is like this: -** -** goto B -** A: setup for the SELECT -** loop over the tables in the SELECT -** gosub C -** end loop -** cleanup after the SELECT -** goto D -** C: insert the select result into the intermediate table -** return -** B: open a cursor to an intermediate table -** goto A -** D: open write cursor to <table> and its indices -** loop over the intermediate table -** transfer values form intermediate table into <table> -** end the loop -** cleanup -*/ -void sqlite3Insert( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* Name of table into which we are inserting */ - ExprList *pList, /* List of values to be inserted */ - Select *pSelect, /* A SELECT statement to use as the data source */ - IdList *pColumn, /* Column names corresponding to IDLIST. */ - int onError /* How to handle constraint errors */ -){ - Table *pTab; /* The table to insert into */ - char *zTab; /* Name of the table into which we are inserting */ - const char *zDb; /* Name of the database holding this table */ - int i, j, idx; /* Loop counters */ - Vdbe *v; /* Generate code into this virtual machine */ - Index *pIdx; /* For looping over indices of the table */ - int nColumn; /* Number of columns in the data */ - int base = 0; /* VDBE Cursor number for pTab */ - int iCont=0,iBreak=0; /* Beginning and end of the loop over srcTab */ - sqlite3 *db; /* The main database structure */ - int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ - int endOfLoop; /* Label for the end of the insertion loop */ - int useTempTable; /* Store SELECT results in intermediate table */ - int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ - int iSelectLoop = 0; /* Address of code that implements the SELECT */ - int iCleanup = 0; /* Address of the cleanup code */ - int iInsertBlock = 0; /* Address of the subroutine used to insert data */ - int iCntMem = 0; /* Memory cell used for the row counter */ - int isView; /* True if attempting to insert into a view */ - - int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ - int before_triggers; /* True if there are BEFORE triggers */ - int after_triggers; /* True if there are AFTER triggers */ - int newIdx = -1; /* Cursor for the NEW table */ - - if( pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup; - db = pParse->db; - - /* Locate the table into which we will be inserting new information. - */ - assert( pTabList->nSrc==1 ); - zTab = pTabList->a[0].zName; - if( zTab==0 ) goto insert_cleanup; - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ){ - goto insert_cleanup; - } - assert( pTab->iDb<db->nDb ); - zDb = db->aDb[pTab->iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ - goto insert_cleanup; - } - - /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist - */ - before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT, - TK_BEFORE, TK_ROW, 0); - after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT, - TK_AFTER, TK_ROW, 0); - row_triggers_exist = before_triggers || after_triggers; - isView = pTab->pSelect!=0; - if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){ - goto insert_cleanup; - } - if( pTab==0 ) goto insert_cleanup; - - /* If pTab is really a view, make sure it has been initialized. - */ - if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto insert_cleanup; - } - - /* Ensure all required collation sequences are available. */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){ - goto insert_cleanup; - } - } - - /* Allocate a VDBE - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto insert_cleanup; - sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb); - - /* if there are row triggers, allocate a temp table for new.* references. */ - if( row_triggers_exist ){ - newIdx = pParse->nTab++; - } - - /* Figure out how many columns of data are supplied. If the data - ** is coming from a SELECT statement, then this step also generates - ** all the code to implement the SELECT statement and invoke a subroutine - ** to process each row of the result. (Template 2.) If the SELECT - ** statement uses the the table that is being inserted into, then the - ** subroutine is also coded here. That subroutine stores the SELECT - ** results in a temporary table. (Template 3.) - */ - if( pSelect ){ - /* Data is coming from a SELECT. Generate code to implement that SELECT - */ - int rc, iInitCode; - iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); - iSelectLoop = sqlite3VdbeCurrentAddr(v); - iInsertBlock = sqlite3VdbeMakeLabel(v); - rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0,0); - if( rc || pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup; - iCleanup = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup); - assert( pSelect->pEList ); - nColumn = pSelect->pEList->nExpr; - - /* Set useTempTable to TRUE if the result of the SELECT statement - ** should be written into a temporary table. Set to FALSE if each - ** row of the SELECT can be written directly into the result table. - ** - ** A temp table must be used if the table being updated is also one - ** of the tables being read by the SELECT statement. Also use a - ** temp table in the case of row triggers. - */ - if( row_triggers_exist ){ - useTempTable = 1; - }else{ - int addr = 0; - useTempTable = 0; - while( useTempTable==0 ){ - VdbeOp *pOp; - addr = sqlite3VdbeFindOp(v, addr, OP_OpenRead, pTab->tnum); - if( addr==0 ) break; - pOp = sqlite3VdbeGetOp(v, addr-2); - if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){ - useTempTable = 1; - } - } - } - - if( useTempTable ){ - /* Generate the subroutine that SELECT calls to process each row of - ** the result. Store the result in a temporary table - */ - srcTab = pParse->nTab++; - sqlite3VdbeResolveLabel(v, iInsertBlock); - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); - sqlite3TableAffinityStr(v, pTab); - sqlite3VdbeAddOp(v, OP_NewRecno, srcTab, 0); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, srcTab, 0); - sqlite3VdbeAddOp(v, OP_Return, 0, 0); - - /* The following code runs first because the GOTO at the very top - ** of the program jumps to it. Create the temporary table, then jump - ** back up and execute the SELECT code above. - */ - sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp(v, OP_OpenTemp, srcTab, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn); - sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop); - sqlite3VdbeResolveLabel(v, iCleanup); - }else{ - sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v)); - } - }else{ - /* This is the case if the data for the INSERT is coming from a VALUES - ** clause - */ - SrcList dummy; - assert( pList!=0 ); - srcTab = -1; - useTempTable = 0; - assert( pList ); - nColumn = pList->nExpr; - dummy.nSrc = 0; - for(i=0; i<nColumn; i++){ - if( sqlite3ExprResolveAndCheck(pParse,&dummy,0,pList->a[i].pExpr,0,0) ){ - goto insert_cleanup; - } - } - } - - /* Make sure the number of columns in the source data matches the number - ** of columns to be inserted into the table. - */ - if( pColumn==0 && nColumn!=pTab->nCol ){ - sqlite3ErrorMsg(pParse, - "table %S has %d columns but %d values were supplied", - pTabList, 0, pTab->nCol, nColumn); - goto insert_cleanup; - } - if( pColumn!=0 && nColumn!=pColumn->nId ){ - sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); - goto insert_cleanup; - } - - /* If the INSERT statement included an IDLIST term, then make sure - ** all elements of the IDLIST really are columns of the table and - ** remember the column indices. - ** - ** If the table has an INTEGER PRIMARY KEY column and that column - ** is named in the IDLIST, then record in the keyColumn variable - ** the index into IDLIST of the primary key column. keyColumn is - ** the index of the primary key as it appears in IDLIST, not as - ** is appears in the original table. (The index of the primary - ** key in the original table is pTab->iPKey.) - */ - if( pColumn ){ - for(i=0; i<pColumn->nId; i++){ - pColumn->a[i].idx = -1; - } - for(i=0; i<pColumn->nId; i++){ - for(j=0; j<pTab->nCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ - pColumn->a[i].idx = j; - if( j==pTab->iPKey ){ - keyColumn = i; - } - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pColumn->a[i].zName) ){ - keyColumn = i; - }else{ - sqlite3ErrorMsg(pParse, "table %S has no column named %s", - pTabList, 0, pColumn->a[i].zName); - pParse->nErr++; - goto insert_cleanup; - } - } - } - } - - /* If there is no IDLIST term but the table has an integer primary - ** key, the set the keyColumn variable to the primary key column index - ** in the original table definition. - */ - if( pColumn==0 ){ - keyColumn = pTab->iPKey; - } - - /* Open the temp table for FOR EACH ROW triggers - */ - if( row_triggers_exist ){ - sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol); - } - - /* Initialize the count of rows to be inserted - */ - if( db->flags & SQLITE_CountRows ){ - iCntMem = pParse->nMem++; - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp(v, OP_MemStore, iCntMem, 1); - } - - /* Open tables and indices if there are no row triggers */ - if( !row_triggers_exist ){ - base = pParse->nTab; - sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite); - } - - /* If the data source is a temporary table, then we have to create - ** a loop because there might be multiple rows of data. If the data - ** source is a subroutine call from the SELECT statement, then we need - ** to launch the SELECT statement processing. - */ - if( useTempTable ){ - iBreak = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Rewind, srcTab, iBreak); - iCont = sqlite3VdbeCurrentAddr(v); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop); - sqlite3VdbeResolveLabel(v, iInsertBlock); - } - - /* Run the BEFORE and INSTEAD OF triggers, if there are any - */ - endOfLoop = sqlite3VdbeMakeLabel(v); - if( before_triggers ){ - - /* build the NEW.* reference row. Note that if there is an INTEGER - ** PRIMARY KEY into which a NULL is being inserted, that NULL will be - ** translated into a unique ID for the row. But on a BEFORE trigger, - ** we do not know what the unique ID will be (because the insert has - ** not happened yet) so we substitute a rowid of -1 - */ - if( keyColumn<0 ){ - sqlite3VdbeAddOp(v, OP_Integer, -1, 0); - }else if( useTempTable ){ - sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1); - }else{ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr); - sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - sqlite3VdbeAddOp(v, OP_Integer, -1, 0); - sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); - } - - /* Create the new column data - */ - for(i=0; i<pTab->nCol; i++){ - if( pColumn==0 ){ - j = i; - }else{ - for(j=0; j<pColumn->nId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( pColumn && j>=pColumn->nId ){ - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); - }else if( useTempTable ){ - sqlite3VdbeAddOp(v, OP_Column, srcTab, j); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Dup, nColumn-j-1, 1); - }else{ - sqlite3ExprCode(pParse, pList->a[j].pExpr); - } - } - sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); - - /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, - ** do not attempt any conversions before assembling the record. - ** If this is a real table, attempt conversions as required by the - ** table column affinities. - */ - if( !isView ){ - sqlite3TableAffinityStr(v, pTab); - } - sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0); - - /* Fire BEFORE or INSTEAD OF triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, - newIdx, -1, onError, endOfLoop) ){ - goto insert_cleanup; - } - } - - /* If any triggers exists, the opening of tables and indices is deferred - ** until now. - */ - if( row_triggers_exist && !isView ){ - base = pParse->nTab; - sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite); - } - - /* Push the record number for the new entry onto the stack. The - ** record number is a randomly generate integer created by NewRecno - ** except when the table has an INTEGER PRIMARY KEY column, in which - ** case the record number is the same as that column. - */ - if( !isView ){ - if( keyColumn>=0 ){ - if( useTempTable ){ - sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1); - }else{ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr); - } - /* If the PRIMARY KEY expression is NULL, then use OP_NewRecno - ** to generate a unique primary key value. - */ - sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - sqlite3VdbeAddOp(v, OP_NewRecno, base, 0); - sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); - }else{ - sqlite3VdbeAddOp(v, OP_NewRecno, base, 0); - } - - /* Push onto the stack, data for all columns of the new entry, beginning - ** with the first column. - */ - for(i=0; i<pTab->nCol; i++){ - if( i==pTab->iPKey ){ - /* The value of the INTEGER PRIMARY KEY column is always a NULL. - ** Whenever this column is read, the record number will be substituted - ** in its place. So will fill this column with a NULL to avoid - ** taking up data space with information that will never be used. */ - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - continue; - } - if( pColumn==0 ){ - j = i; - }else{ - for(j=0; j<pColumn->nId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( pColumn && j>=pColumn->nId ){ - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); - }else if( useTempTable ){ - sqlite3VdbeAddOp(v, OP_Column, srcTab, j); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Dup, i+nColumn-j, 1); - }else{ - sqlite3ExprCode(pParse, pList->a[j].pExpr); - } - } - - /* Generate code to check constraints and generate index keys and - ** do the insertion. - */ - sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0, - 0, onError, endOfLoop); - sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0, - after_triggers ? newIdx : -1); - } - - /* Update the count of rows that are inserted - */ - if( (db->flags & SQLITE_CountRows)!=0 ){ - sqlite3VdbeAddOp(v, OP_MemIncr, iCntMem, 0); - } - - if( row_triggers_exist ){ - /* Close all tables opened */ - if( !isView ){ - sqlite3VdbeAddOp(v, OP_Close, base, 0); - for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqlite3VdbeAddOp(v, OP_Close, idx+base, 0); - } - } - - /* Code AFTER triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, - onError, endOfLoop) ){ - goto insert_cleanup; - } - } - - /* The bottom of the loop, if the data source is a SELECT statement - */ - sqlite3VdbeResolveLabel(v, endOfLoop); - if( useTempTable ){ - sqlite3VdbeAddOp(v, OP_Next, srcTab, iCont); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp(v, OP_Close, srcTab, 0); - }else if( pSelect ){ - sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0); - sqlite3VdbeAddOp(v, OP_Return, 0, 0); - sqlite3VdbeResolveLabel(v, iCleanup); - } - - if( !row_triggers_exist ){ - /* Close all tables opened */ - sqlite3VdbeAddOp(v, OP_Close, base, 0); - for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqlite3VdbeAddOp(v, OP_Close, idx+base, 0); - } - } - - /* - ** Return the number of rows inserted. - */ - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp(v, OP_MemLoad, iCntMem, 0); - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "rows inserted", P3_STATIC); - } - -insert_cleanup: - sqlite3SrcListDelete(pTabList); - if( pList ) sqlite3ExprListDelete(pList); - if( pSelect ) sqlite3SelectDelete(pSelect); - sqlite3IdListDelete(pColumn); -} - -/* -** Generate code to do a constraint check prior to an INSERT or an UPDATE. -** -** When this routine is called, the stack contains (from bottom to top) -** the following values: -** -** 1. The recno of the row to be updated before the update. This -** value is omitted unless we are doing an UPDATE that involves a -** change to the record number. -** -** 2. The recno of the row after the update. -** -** 3. The data in the first column of the entry after the update. -** -** i. Data from middle columns... -** -** N. The data in the last column of the entry after the update. -** -** The old recno shown as entry (1) above is omitted unless both isUpdate -** and recnoChng are 1. isUpdate is true for UPDATEs and false for -** INSERTs and recnoChng is true if the record number is being changed. -** -** The code generated by this routine pushes additional entries onto -** the stack which are the keys for new index entries for the new record. -** The order of index keys is the same as the order of the indices on -** the pTable->pIndex list. A key is only created for index i if -** aIdxUsed!=0 and aIdxUsed[i]!=0. -** -** This routine also generates code to check constraints. NOT NULL, -** CHECK, and UNIQUE constraints are all checked. If a constraint fails, -** then the appropriate action is performed. There are five possible -** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE. -** -** Constraint type Action What Happens -** --------------- ---------- ---------------------------------------- -** any ROLLBACK The current transaction is rolled back and -** sqlite3_exec() returns immediately with a -** return code of SQLITE_CONSTRAINT. -** -** any ABORT Back out changes from the current command -** only (do not do a complete rollback) then -** cause sqlite3_exec() to return immediately -** with SQLITE_CONSTRAINT. -** -** any FAIL Sqlite_exec() returns immediately with a -** return code of SQLITE_CONSTRAINT. The -** transaction is not rolled back and any -** prior changes are retained. -** -** any IGNORE The record number and data is popped from -** the stack and there is an immediate jump -** to label ignoreDest. -** -** NOT NULL REPLACE The NULL value is replace by the default -** value for that column. If the default value -** is NULL, the action is the same as ABORT. -** -** UNIQUE REPLACE The other row that conflicts with the row -** being inserted is removed. -** -** CHECK REPLACE Illegal. The results in an exception. -** -** Which action to take is determined by the overrideError parameter. -** Or if overrideError==OE_Default, then the pParse->onError parameter -** is used. Or if pParse->onError==OE_Default then the onError value -** for the constraint is used. -** -** The calling routine must open a read/write cursor for pTab with -** cursor number "base". All indices of pTab must also have open -** read/write cursors with cursor number base+i for the i-th cursor. -** Except, if there is no possibility of a REPLACE action then -** cursors do not need to be open for indices where aIdxUsed[i]==0. -** -** If the isUpdate flag is true, it means that the "base" cursor is -** initially pointing to an entry that is being updated. The isUpdate -** flag causes extra code to be generated so that the "base" cursor -** is still pointing at the same entry after the routine returns. -** Without the isUpdate flag, the "base" cursor might be moved. -*/ -void sqlite3GenerateConstraintChecks( - Parse *pParse, /* The parser context */ - Table *pTab, /* the table into which we are inserting */ - int base, /* Index of a read/write cursor pointing at pTab */ - char *aIdxUsed, /* Which indices are used. NULL means all are used */ - int recnoChng, /* True if the record number will change */ - int isUpdate, /* True for UPDATE, False for INSERT */ - int overrideError, /* Override onError to this if not OE_Default */ - int ignoreDest /* Jump to this label on an OE_Ignore resolution */ -){ - int i; - Vdbe *v; - int nCol; - int onError; - int addr; - int extra; - int iCur; - Index *pIdx; - int seenReplace = 0; - int jumpInst1=0, jumpInst2; - int contAddr; - int hasTwoRecnos = (isUpdate && recnoChng); - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ - nCol = pTab->nCol; - - /* Test all NOT NULL constraints. - */ - for(i=0; i<nCol; i++){ - if( i==pTab->iPKey ){ - continue; - } - onError = pTab->aCol[i].notNull; - if( onError==OE_None ) continue; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){ - onError = OE_Abort; - } - sqlite3VdbeAddOp(v, OP_Dup, nCol-1-i, 1); - addr = sqlite3VdbeAddOp(v, OP_NotNull, 1, 0); - switch( onError ){ - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - char *zMsg = 0; - sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError); - sqlite3SetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName, - " may not be NULL", (char*)0); - sqlite3VdbeChangeP3(v, -1, zMsg, P3_DYNAMIC); - break; - } - case OE_Ignore: { - sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); - break; - } - case OE_Replace: { - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); - sqlite3VdbeAddOp(v, OP_Push, nCol-i, 0); - break; - } - default: assert(0); - } - sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v)); - } - - /* Test all CHECK constraints - */ - /**** TBD ****/ - - /* If we have an INTEGER PRIMARY KEY, make sure the primary key - ** of the new record does not previously exist. Except, if this - ** is an UPDATE and the primary key is not changing, that is OK. - */ - if( recnoChng ){ - onError = pTab->keyConf; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - - if( isUpdate ){ - sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); - sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); - jumpInst1 = sqlite3VdbeAddOp(v, OP_Eq, 0, 0); - } - sqlite3VdbeAddOp(v, OP_Dup, nCol, 1); - jumpInst2 = sqlite3VdbeAddOp(v, OP_NotExists, base, 0); - switch( onError ){ - default: { - onError = OE_Abort; - /* Fall thru into the next case */ - } - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, - "PRIMARY KEY must be unique", P3_STATIC); - break; - } - case OE_Replace: { - sqlite3GenerateRowIndexDelete(pParse->db, v, pTab, base, 0); - if( isUpdate ){ - sqlite3VdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1); - sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); - } - seenReplace = 1; - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); - break; - } - } - contAddr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeChangeP2(v, jumpInst2, contAddr); - if( isUpdate ){ - sqlite3VdbeChangeP2(v, jumpInst1, contAddr); - sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1); - sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); - } - } - - /* Test all UNIQUE constraints by creating entries for each UNIQUE - ** index and making sure that duplicate entries do not already exist. - ** Add the new records to the indices as we go. - */ - extra = -1; - for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ - if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */ - extra++; - - /* Create a key for accessing the index entry */ - sqlite3VdbeAddOp(v, OP_Dup, nCol+extra, 1); - for(i=0; i<pIdx->nColumn; i++){ - int idx = pIdx->aiColumn[i]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1); - }else{ - sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1); - } - } - jumpInst1 = sqlite3VdbeAddOp(v, OP_MakeRecord, pIdx->nColumn, (1<<24)); - sqlite3IndexAffinityStr(v, pIdx); - - /* Find out what action to take in case there is an indexing conflict */ - onError = pIdx->onError; - if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */ - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( seenReplace ){ - if( onError==OE_Ignore ) onError = OE_Replace; - else if( onError==OE_Fail ) onError = OE_Abort; - } - - - /* Check to see if the new index entry will be unique */ - sqlite3VdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1); - jumpInst2 = sqlite3VdbeAddOp(v, OP_IsUnique, base+iCur+1, 0); - - /* Generate code that executes if the new index entry is not unique */ - switch( onError ){ - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - int j, n1, n2; - char zErrMsg[200]; - strcpy(zErrMsg, pIdx->nColumn>1 ? "columns " : "column "); - n1 = strlen(zErrMsg); - for(j=0; j<pIdx->nColumn && n1<sizeof(zErrMsg)-30; j++){ - char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; - n2 = strlen(zCol); - if( j>0 ){ - strcpy(&zErrMsg[n1], ", "); - n1 += 2; - } - if( n1+n2>sizeof(zErrMsg)-30 ){ - strcpy(&zErrMsg[n1], "..."); - n1 += 3; - break; - }else{ - strcpy(&zErrMsg[n1], zCol); - n1 += n2; - } - } - strcpy(&zErrMsg[n1], - pIdx->nColumn>1 ? " are not unique" : " is not unique"); - sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0); - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest); - break; - } - case OE_Replace: { - sqlite3GenerateRowDelete(pParse->db, v, pTab, base, 0); - if( isUpdate ){ - sqlite3VdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1); - sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); - } - seenReplace = 1; - break; - } - default: assert(0); - } - contAddr = sqlite3VdbeCurrentAddr(v); - assert( contAddr<(1<<24) ); -#if NULL_DISTINCT_FOR_UNIQUE - sqlite3VdbeChangeP2(v, jumpInst1, contAddr | (1<<24)); -#endif - sqlite3VdbeChangeP2(v, jumpInst2, contAddr); - } -} - -/* -** This routine generates code to finish the INSERT or UPDATE operation -** that was started by a prior call to sqlite3GenerateConstraintChecks. -** The stack must contain keys for all active indices followed by data -** and the recno for the new entry. This routine creates the new -** entries in all indices and in the main table. -** -** The arguments to this routine should be the same as the first six -** arguments to sqlite3GenerateConstraintChecks. -*/ -void sqlite3CompleteInsertion( - Parse *pParse, /* The parser context */ - Table *pTab, /* the table into which we are inserting */ - int base, /* Index of a read/write cursor pointing at pTab */ - char *aIdxUsed, /* Which indices are used. NULL means all are used */ - int recnoChng, /* True if the record number will change */ - int isUpdate, /* True for UPDATE, False for INSERT */ - int newIdx /* Index of NEW table for triggers. -1 if none */ -){ - int i; - Vdbe *v; - int nIdx; - Index *pIdx; - int pik_flags; - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - for(i=nIdx-1; i>=0; i--){ - if( aIdxUsed && aIdxUsed[i]==0 ) continue; - sqlite3VdbeAddOp(v, OP_IdxPut, base+i+1, 0); - } - sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); - sqlite3TableAffinityStr(v, pTab); - if( newIdx>=0 ){ - sqlite3VdbeAddOp(v, OP_Dup, 1, 0); - sqlite3VdbeAddOp(v, OP_Dup, 1, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0); - } - pik_flags = (OPFLAG_NCHANGE|(isUpdate?0:OPFLAG_LASTROWID)); - sqlite3VdbeAddOp(v, OP_PutIntKey, base, pik_flags); - - if( isUpdate && recnoChng ){ - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - } -} - -/* -** Generate code that will open cursors for a table and for all -** indices of that table. The "base" parameter is the cursor number used -** for the table. Indices are opened on subsequent cursors. -*/ -void sqlite3OpenTableAndIndices( - Parse *pParse, /* Parsing context */ - Table *pTab, /* Table to be opened */ - int base, /* Cursor number assigned to the table */ - int op /* OP_OpenRead or OP_OpenWrite */ -){ - int i; - Index *pIdx; - Vdbe *v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); - sqlite3VdbeAddOp(v, op, base, pTab->tnum); - VdbeComment((v, "# %s", pTab->zName)); - sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol); - for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0); - sqlite3VdbeOp3(v, op, i+base, pIdx->tnum, - (char*)&pIdx->keyInfo, P3_KEYINFO); - } - if( pParse->nTab<=base+i ){ - pParse->nTab = base+i; - } -} diff --git a/kopete/plugins/statistics/sqlite/legacy.c b/kopete/plugins/statistics/sqlite/legacy.c deleted file mode 100644 index f575f1f0..00000000 --- a/kopete/plugins/statistics/sqlite/legacy.c +++ /dev/null @@ -1,138 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Main file for the SQLite library. The routines in this file -** implement the programmer interface to the library. Routines in -** other files are for internal use by SQLite and should not be -** accessed by users of the library. -** -** $Id$ -*/ - -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> - -/* -** Execute SQL code. Return one of the SQLITE_ success/failure -** codes. Also write an error message into memory obtained from -** malloc() and make *pzErrMsg point to that message. -** -** If the SQL is a query, then for each row in the query result -** the xCallback() function is called. pArg becomes the first -** argument to xCallback(). If xCallback=NULL then no callback -** is invoked, even for queries. -*/ -int sqlite3_exec( - sqlite3 *db, /* The database on which the SQL executes */ - const char *zSql, /* The SQL to be executed */ - sqlite3_callback xCallback, /* Invoke this callback routine */ - void *pArg, /* First argument to xCallback() */ - char **pzErrMsg /* Write error messages here */ -){ - int rc = SQLITE_OK; - const char *zLeftover; - sqlite3_stmt *pStmt = 0; - char **azCols = 0; - - int nRetry = 0; - int nChange = 0; - int nCallback; - - if( zSql==0 ) return SQLITE_OK; - while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ - int nCol; - char **azVals = 0; - - pStmt = 0; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover); - if( rc!=SQLITE_OK ){ - if( pStmt ) sqlite3_finalize(pStmt); - continue; - } - if( !pStmt ){ - /* this happens for a comment or white-space */ - zSql = zLeftover; - continue; - } - - db->nChange += nChange; - nCallback = 0; - - nCol = sqlite3_column_count(pStmt); - azCols = sqliteMalloc(2*nCol*sizeof(const char *)); - if( nCol && !azCols ){ - rc = SQLITE_NOMEM; - goto exec_out; - } - - while( 1 ){ - int i; - rc = sqlite3_step(pStmt); - - /* Invoke the callback function if required */ - if( xCallback && (SQLITE_ROW==rc || - (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ - if( 0==nCallback ){ - for(i=0; i<nCol; i++){ - azCols[i] = (char *)sqlite3_column_name(pStmt, i); - } - nCallback++; - } - if( rc==SQLITE_ROW ){ - azVals = &azCols[nCol]; - for(i=0; i<nCol; i++){ - azVals[i] = (char *)sqlite3_column_text(pStmt, i); - } - } - if( xCallback(pArg, nCol, azVals, azCols) ){ - rc = SQLITE_ABORT; - goto exec_out; - } - } - - if( rc!=SQLITE_ROW ){ - rc = sqlite3_finalize(pStmt); - pStmt = 0; - if( db->pVdbe==0 ){ - nChange = db->nChange; - } - if( rc!=SQLITE_SCHEMA ){ - nRetry = 0; - zSql = zLeftover; - while( isspace((unsigned char)zSql[0]) ) zSql++; - } - break; - } - } - - sqliteFree(azCols); - azCols = 0; - } - -exec_out: - if( pStmt ) sqlite3_finalize(pStmt); - if( azCols ) sqliteFree(azCols); - - if( sqlite3_malloc_failed ){ - rc = SQLITE_NOMEM; - } - if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){ - *pzErrMsg = malloc(1+strlen(sqlite3_errmsg(db))); - if( *pzErrMsg ){ - strcpy(*pzErrMsg, sqlite3_errmsg(db)); - } - }else if( pzErrMsg ){ - *pzErrMsg = 0; - } - - return rc; -} diff --git a/kopete/plugins/statistics/sqlite/lempar.c b/kopete/plugins/statistics/sqlite/lempar.c deleted file mode 100644 index ee1edbfa..00000000 --- a/kopete/plugins/statistics/sqlite/lempar.c +++ /dev/null @@ -1,687 +0,0 @@ -/* Driver template for the LEMON parser generator. -** The author disclaims copyright to this source code. -*/ -/* First off, code is include which follows the "include" declaration -** in the input file. */ -#include <stdio.h> -%% -/* Next is all token values, in a form suitable for use by makeheaders. -** This section will be null unless lemon is run with the -m switch. -*/ -/* -** These constants (all generated automatically by the parser generator) -** specify the various kinds of tokens (terminals) that the parser -** understands. -** -** Each symbol here is a terminal symbol in the grammar. -*/ -%% -/* Make sure the INTERFACE macro is defined. -*/ -#ifndef INTERFACE -# define INTERFACE 1 -#endif -/* The next thing included is series of defines which control -** various aspects of the generated parser. -** YYCODETYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 terminals -** and nonterminals. "int" is used otherwise. -** YYNOCODE is a number of type YYCODETYPE which corresponds -** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash -** table. -** YYFALLBACK If defined, this indicates that one or more tokens -** have fall-back values which should be used if the -** original value of the token will not parse. -** YYACTIONTYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 rules and -** states combined. "int" is used otherwise. -** ParseTOKENTYPE is the data type used for minor tokens given -** directly to the parser from the tokenizer. -** YYMINORTYPE is the data type used for all minor tokens. -** This is typically a union of many types, one of -** which is ParseTOKENTYPE. The entry in the union -** for base tokens is called "yy0". -** YYSTACKDEPTH is the maximum depth of the parser's stack. -** ParseARG_SDECL A static variable declaration for the %extra_argument -** ParseARG_PDECL A parameter declaration for the %extra_argument -** ParseARG_STORE Code to store %extra_argument into yypParser -** ParseARG_FETCH Code to extract %extra_argument from yypParser -** YYNSTATE the combined number of states. -** YYNRULE the number of rules in the grammar -** YYERRORSYMBOL is the code number of the error symbol. If not -** defined, then do no error processing. -*/ -%% -#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) -#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) -#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) - -/* Next are that tables used to determine what action to take based on the -** current state and lookahead token. These tables are used to implement -** functions that take a state number and lookahead value and return an -** action integer. -** -** Suppose the action integer is N. Then the action is determined as -** follows -** -** 0 <= N < YYNSTATE Shift N. That is, push the lookahead -** token onto the stack and goto state N. -** -** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. -** -** N == YYNSTATE+YYNRULE A syntax error has occurred. -** -** N == YYNSTATE+YYNRULE+1 The parser accepts its input. -** -** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused -** slots in the yy_action[] table. -** -** The action table is constructed as a single large table named yy_action[]. -** Given state S and lookahead X, the action is computed as -** -** yy_action[ yy_shift_ofst[S] + X ] -** -** If the index value yy_shift_ofst[S]+X is out of range or if the value -** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] -** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. -** -** The formula above is for computing the action when the lookahead is -** a terminal symbol. If the lookahead is a non-terminal (as occurs after -** a reduce action) then the yy_reduce_ofst[] array is used in place of -** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of -** YY_SHIFT_USE_DFLT. -** -** The following are the tables generated in this section: -** -** yy_action[] A single table containing all actions. -** yy_lookahead[] A table containing the lookahead for each entry in -** yy_action. Used to detect hash collisions. -** yy_shift_ofst[] For each state, the offset into yy_action for -** shifting terminals. -** yy_reduce_ofst[] For each state, the offset into yy_action for -** shifting non-terminals after a reduce. -** yy_default[] Default action for each state. -*/ -%% -#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) - -/* The next table maps tokens into fallback tokens. If a construct -** like the following: -** -** %fallback ID X Y Z. -** -** appears in the grammer, then ID becomes a fallback token for X, Y, -** and Z. Whenever one of the tokens X, Y, or Z is input to the parser -** but it does not parse, the type of the token is changed to ID and -** the parse is retried before an error is thrown. -*/ -#ifdef YYFALLBACK -static const YYCODETYPE yyFallback[] = { -%% -}; -#endif /* YYFALLBACK */ - -/* The following structure represents a single element of the -** parser's stack. Information stored includes: -** -** + The state number for the parser at this level of the stack. -** -** + The value of the token stored at this level of the stack. -** (In other words, the "major" token.) -** -** + The semantic value stored at this level of the stack. This is -** the information used by the action routines in the grammar. -** It is sometimes called the "minor" token. -*/ -struct yyStackEntry { - int stateno; /* The state-number */ - int major; /* The major token value. This is the code - ** number for the token at this stack level */ - YYMINORTYPE minor; /* The user-supplied minor token value. This - ** is the value of the token */ -}; -typedef struct yyStackEntry yyStackEntry; - -/* The state of the parser is completely contained in an instance of -** the following structure */ -struct yyParser { - int yyidx; /* Index of top element in stack */ - int yyerrcnt; /* Shifts left before out of the error */ - ParseARG_SDECL /* A place to hold %extra_argument */ - yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ -}; -typedef struct yyParser yyParser; - -#ifndef NDEBUG -#include <stdio.h> -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* -** Turn parser tracing on by giving a stream to which to write the trace -** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL -** -** Inputs: -** <ul> -** <li> A FILE* to which trace output should be written. -** If NULL, then tracing is turned off. -** <li> A prefix string written at the beginning of every -** line of trace output. If NULL, then tracing is -** turned off. -** </ul> -** -** Outputs: -** None. -*/ -void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ - yyTraceFILE = TraceFILE; - yyTracePrompt = zTracePrompt; - if( yyTraceFILE==0 ) yyTracePrompt = 0; - else if( yyTracePrompt==0 ) yyTraceFILE = 0; -} -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing shifts, the names of all terminals and nonterminals -** are required. The following table supplies these names */ -static const char *yyTokenName[] = { -%% -}; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing reduce actions, the names of all rules are required. -*/ -static const char *yyRuleName[] = { -%% -}; -#endif /* NDEBUG */ - -/* -** This function returns the symbolic name associated with a token -** value. -*/ -const char *ParseTokenName(int tokenType){ -#ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ - return yyTokenName[tokenType]; - }else{ - return "Unknown"; - } -#else - return ""; -#endif -} - -/* -** This function allocates a new parser. -** The only argument is a pointer to a function which works like -** malloc. -** -** Inputs: -** A pointer to the function used to allocate memory. -** -** Outputs: -** A pointer to a parser. This pointer is used in subsequent calls -** to Parse and ParseFree. -*/ -void *ParseAlloc(void *(*mallocProc)(size_t)){ - yyParser *pParser; - pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); - if( pParser ){ - pParser->yyidx = -1; - } - return pParser; -} - -/* The following function deletes the value associated with a -** symbol. The symbol can be either a terminal or nonterminal. -** "yymajor" is the symbol code, and "yypminor" is a pointer to -** the value. -*/ -static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ - switch( yymajor ){ - /* Here is inserted the actions which take place when a - ** terminal or non-terminal is destroyed. This can happen - ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is - ** being destroyed before it is finished parsing. - ** - ** Note: during a reduce, the only symbols destroyed are those - ** which appear on the RHS of the rule, but which are not used - ** inside the C code. - */ -%% - default: break; /* If no destructor action specified: do nothing */ - } -} - -/* -** Pop the parser's stack once. -** -** If there is a destructor routine associated with the token which -** is popped from the stack, then call it. -** -** Return the major token number for the symbol popped. -*/ -static int yy_pop_parser_stack(yyParser *pParser){ - YYCODETYPE yymajor; - yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; - - if( pParser->yyidx<0 ) return 0; -#ifndef NDEBUG - if( yyTraceFILE && pParser->yyidx>=0 ){ - fprintf(yyTraceFILE,"%sPopping %s\n", - yyTracePrompt, - yyTokenName[yytos->major]); - } -#endif - yymajor = yytos->major; - yy_destructor( yymajor, &yytos->minor); - pParser->yyidx--; - return yymajor; -} - -/* -** Deallocate and destroy a parser. Destructors are all called for -** all stack elements before shutting the parser down. -** -** Inputs: -** <ul> -** <li> A pointer to the parser. This should be a pointer -** obtained from ParseAlloc. -** <li> A pointer to a function used to reclaim memory obtained -** from malloc. -** </ul> -*/ -void ParseFree( - void *p, /* The parser to be deleted */ - void (*freeProc)(void*) /* Function used to reclaim memory */ -){ - yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; - while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); - (*freeProc)((void*)pParser); -} - -/* -** Find the appropriate action for a parser given the terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_shift_action( - yyParser *pParser, /* The parser */ - int iLookAhead /* The look-ahead token */ -){ - int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; - - /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ - i = yy_shift_ofst[stateno]; - if( i==YY_SHIFT_USE_DFLT ){ - return yy_default[stateno]; - } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } - i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ -#ifdef YYFALLBACK - int iFallback; /* Fallback token */ - if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) - && (iFallback = yyFallback[iLookAhead])!=0 ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", - yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); - } -#endif - return yy_find_shift_action(pParser, iFallback); - } -#endif - return yy_default[stateno]; - }else{ - return yy_action[i]; - } -} - -/* -** Find the appropriate action for a parser given the non-terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_reduce_action( - yyParser *pParser, /* The parser */ - int iLookAhead /* The look-ahead token */ -){ - int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; - - i = yy_reduce_ofst[stateno]; - if( i==YY_REDUCE_USE_DFLT ){ - return yy_default[stateno]; - } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } - i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ - return yy_default[stateno]; - }else{ - return yy_action[i]; - } -} - -/* -** Perform a shift action. -*/ -static void yy_shift( - yyParser *yypParser, /* The parser to be shifted */ - int yyNewState, /* The new state to shift in */ - int yyMajor, /* The major token to shift in */ - YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ -){ - yyStackEntry *yytos; - yypParser->yyidx++; - if( yypParser->yyidx>=YYSTACKDEPTH ){ - ParseARG_FETCH; - yypParser->yyidx--; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will execute if the parser - ** stack every overflows */ -%% - ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ - return; - } - yytos = &yypParser->yystack[yypParser->yyidx]; - yytos->stateno = yyNewState; - yytos->major = yyMajor; - yytos->minor = *yypMinor; -#ifndef NDEBUG - if( yyTraceFILE && yypParser->yyidx>0 ){ - int i; - fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); - fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); - for(i=1; i<=yypParser->yyidx; i++) - fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); - fprintf(yyTraceFILE,"\n"); - } -#endif -} - -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static struct { - YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - unsigned char nrhs; /* Number of right-hand side symbols in the rule */ -} yyRuleInfo[] = { -%% -}; - -static void yy_accept(yyParser*); /* Forward Declaration */ - -/* -** Perform a reduce action and the shift that must immediately -** follow the reduce. -*/ -static void yy_reduce( - yyParser *yypParser, /* The parser */ - int yyruleno /* Number of the rule by which to reduce */ -){ - int yygoto; /* The next state */ - int yyact; /* The next action */ - YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ - yyStackEntry *yymsp; /* The top of the parser's stack */ - int yysize; /* Amount to pop the stack */ - ParseARG_FETCH; - yymsp = &yypParser->yystack[yypParser->yyidx]; -#ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ - fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, - yyRuleName[yyruleno]); - } -#endif /* NDEBUG */ - - switch( yyruleno ){ - /* Beginning here are the reduction cases. A typical example - ** follows: - ** case 0: - ** #line <lineno> <grammarfile> - ** { ... } // User supplied code - ** #line <lineno> <thisfile> - ** break; - */ -%% - }; - yygoto = yyRuleInfo[yyruleno].lhs; - yysize = yyRuleInfo[yyruleno].nrhs; - yypParser->yyidx -= yysize; - yyact = yy_find_reduce_action(yypParser,yygoto); - if( yyact < YYNSTATE ){ - yy_shift(yypParser,yyact,yygoto,&yygotominor); - }else if( yyact == YYNSTATE + YYNRULE + 1 ){ - yy_accept(yypParser); - } -} - -/* -** The following code executes when the parse fails -*/ -static void yy_parse_failed( - yyParser *yypParser /* The parser */ -){ - ParseARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser fails */ -%% - ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* -** The following code executes when a syntax error first occurs. -*/ -static void yy_syntax_error( - yyParser *yypParser, /* The parser */ - int yymajor, /* The major type of the error token */ - YYMINORTYPE yyminor /* The minor type of the error token */ -){ - ParseARG_FETCH; -#define TOKEN (yyminor.yy0) -%% - ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* -** The following is executed when the parser accepts -*/ -static void yy_accept( - yyParser *yypParser /* The parser */ -){ - ParseARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser accepts */ -%% - ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* The main parser program. -** The first argument is a pointer to a structure obtained from -** "ParseAlloc" which describes the current state of the parser. -** The second argument is the major token number. The third is -** the minor token. The fourth optional argument is whatever the -** user wants (and specified in the grammar) and is available for -** use by the action routines. -** -** Inputs: -** <ul> -** <li> A pointer to the parser (an opaque structure.) -** <li> The major token number. -** <li> The minor token number. -** <li> An option argument of a grammar-specified type. -** </ul> -** -** Outputs: -** None. -*/ -void Parse( - void *yyp, /* The parser */ - int yymajor, /* The major token code number */ - ParseTOKENTYPE yyminor /* The value for the token */ - ParseARG_PDECL /* Optional %extra_argument parameter */ -){ - YYMINORTYPE yyminorunion; - int yyact; /* The parser action. */ - int yyendofinput; /* True if we are at the end of input */ - int yyerrorhit = 0; /* True if yymajor has invoked an error */ - yyParser *yypParser; /* The parser */ - - /* (re)initialize the parser, if necessary */ - yypParser = (yyParser*)yyp; - if( yypParser->yyidx<0 ){ - if( yymajor==0 ) return; - yypParser->yyidx = 0; - yypParser->yyerrcnt = -1; - yypParser->yystack[0].stateno = 0; - yypParser->yystack[0].major = 0; - } - yyminorunion.yy0 = yyminor; - yyendofinput = (yymajor==0); - ParseARG_STORE; - -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); - } -#endif - - do{ - yyact = yy_find_shift_action(yypParser,yymajor); - if( yyact<YYNSTATE ){ - yy_shift(yypParser,yyact,yymajor,&yyminorunion); - yypParser->yyerrcnt--; - if( yyendofinput && yypParser->yyidx>=0 ){ - yymajor = 0; - }else{ - yymajor = YYNOCODE; - } - }else if( yyact < YYNSTATE + YYNRULE ){ - yy_reduce(yypParser,yyact-YYNSTATE); - }else if( yyact == YY_ERROR_ACTION ){ - int yymx; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); - } -#endif -#ifdef YYERRORSYMBOL - /* A syntax error has occurred. - ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". - ** - ** This is what we do if the grammar does define ERROR: - ** - ** * Call the %syntax_error function. - ** - ** * Begin popping the stack until we enter a state where - ** it is legal to shift the error symbol, then shift - ** the error symbol. - ** - ** * Set the error count to three. - ** - ** * Begin accepting and shifting new tokens. No new error - ** processing will occur until three tokens have been - ** shifted successfully. - ** - */ - if( yypParser->yyerrcnt<0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yymx = yypParser->yystack[yypParser->yyidx].major; - if( yymx==YYERRORSYMBOL || yyerrorhit ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sDiscard input token %s\n", - yyTracePrompt,yyTokenName[yymajor]); - } -#endif - yy_destructor(yymajor,&yyminorunion); - yymajor = YYNOCODE; - }else{ - while( - yypParser->yyidx >= 0 && - yymx != YYERRORSYMBOL && - (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE - ){ - yy_pop_parser_stack(yypParser); - } - if( yypParser->yyidx < 0 || yymajor==0 ){ - yy_destructor(yymajor,&yyminorunion); - yy_parse_failed(yypParser); - yymajor = YYNOCODE; - }else if( yymx!=YYERRORSYMBOL ){ - YYMINORTYPE u2; - u2.YYERRSYMDT = 0; - yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); - } - } - yypParser->yyerrcnt = 3; - yyerrorhit = 1; -#else /* YYERRORSYMBOL is not defined */ - /* This is what we do if the grammar does not define ERROR: - ** - ** * Report an error message, and throw away the input token. - ** - ** * If the input token is $, then fail the parse. - ** - ** As before, subsequent error messages are suppressed until - ** three input tokens have been successfully shifted. - */ - if( yypParser->yyerrcnt<=0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yypParser->yyerrcnt = 3; - yy_destructor(yymajor,&yyminorunion); - if( yyendofinput ){ - yy_parse_failed(yypParser); - } - yymajor = YYNOCODE; -#endif - }else{ - yy_accept(yypParser); - yymajor = YYNOCODE; - } - }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); - return; -} diff --git a/kopete/plugins/statistics/sqlite/main.c b/kopete/plugins/statistics/sqlite/main.c deleted file mode 100644 index 0ae7e1b2..00000000 --- a/kopete/plugins/statistics/sqlite/main.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Main file for the SQLite library. The routines in this file -** implement the programmer interface to the library. Routines in -** other files are for internal use by SQLite and should not be -** accessed by users of the library. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> - -/* -** The following constant value is used by the SQLITE_BIGENDIAN and -** SQLITE_LITTLEENDIAN macros. -*/ -const int sqlite3one = 1; - -/* -** Fill the InitData structure with an error message that indicates -** that the database is corrupt. -*/ -static void corruptSchema(InitData *pData, const char *zExtra){ - if( !sqlite3_malloc_failed ){ - sqlite3SetString(pData->pzErrMsg, "malformed database schema", - zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0); - } -} - -/* -** This is the callback routine for the code that initializes the -** database. See sqlite3Init() below for additional information. -** This routine is also called from the OP_ParseSchema opcode of the VDBE. -** -** Each callback contains the following information: -** -** argv[0] = name of thing being created -** argv[1] = root page number for table or index. NULL for trigger or view. -** argv[2] = SQL text for the CREATE statement. -** argv[3] = "1" for temporary files, "0" for main database, "2" or more -** for auxiliary database files. -** -*/ -int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){ - InitData *pData = (InitData*)pInit; - sqlite3 *db = pData->db; - int iDb; - - assert( argc==4 ); - if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ - if( argv[1]==0 || argv[3]==0 ){ - corruptSchema(pData, 0); - return 1; - } - iDb = atoi(argv[3]); - assert( iDb>=0 && iDb<db->nDb ); - if( argv[2] && argv[2][0] ){ - /* Call the parser to process a CREATE TABLE, INDEX or VIEW. - ** But because db->init.busy is set to 1, no VDBE code is generated - ** or executed. All the parser does is build the internal data - ** structures that describe the table, index, or view. - */ - char *zErr; - int rc; - assert( db->init.busy ); - db->init.iDb = iDb; - db->init.newTnum = atoi(argv[1]); - rc = sqlite3_exec(db, argv[2], 0, 0, &zErr); - db->init.iDb = 0; - if( SQLITE_OK!=rc ){ - corruptSchema(pData, zErr); - sqlite3_free(zErr); - return rc; - } - }else{ - /* If the SQL column is blank it means this is an index that - ** was created to be the PRIMARY KEY or to fulfill a UNIQUE - ** constraint for a CREATE TABLE. The index should have already - ** been created when we processed the CREATE TABLE. All we have - ** to do here is record the root page number for that index. - */ - Index *pIndex; - pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName); - if( pIndex==0 || pIndex->tnum!=0 ){ - /* This can occur if there exists an index on a TEMP table which - ** has the same name as another index on a permanent index. Since - ** the permanent table is hidden by the TEMP table, we can also - ** safely ignore the index on the permanent table. - */ - /* Do Nothing */; - }else{ - pIndex->tnum = atoi(argv[1]); - } - } - return 0; -} - -/* -** Attempt to read the database schema and initialize internal -** data structures for a single database file. The index of the -** database file is given by iDb. iDb==0 is used for the main -** database. iDb==1 should never be used. iDb>=2 is used for -** auxiliary databases. Return one of the SQLITE_ error codes to -** indicate success or failure. -*/ -static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ - int rc; - BtCursor *curMain; - int size; - Table *pTab; - char const *azArg[5]; - char zDbNum[30]; - int meta[10]; - InitData initData; - char const *zMasterSchema; - char const *zMasterName; - - /* - ** The master database table has a structure like this - */ - static const char master_schema[] = - "CREATE TABLE sqlite_master(\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")" - ; - static const char temp_master_schema[] = - "CREATE TEMP TABLE sqlite_temp_master(\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")" - ; - - assert( iDb>=0 && iDb<db->nDb ); - - /* zMasterSchema and zInitScript are set to point at the master schema - ** and initialisation script appropriate for the database being - ** initialised. zMasterName is the name of the master table. - */ - if( iDb==1 ){ - zMasterSchema = temp_master_schema; - zMasterName = TEMP_MASTER_NAME; - }else{ - zMasterSchema = master_schema; - zMasterName = MASTER_NAME; - } - - /* Construct the schema tables. */ - sqlite3SafetyOff(db); - azArg[0] = zMasterName; - azArg[1] = "1"; - azArg[2] = zMasterSchema; - sprintf(zDbNum, "%d", iDb); - azArg[3] = zDbNum; - azArg[4] = 0; - initData.db = db; - initData.pzErrMsg = pzErrMsg; - rc = sqlite3InitCallback(&initData, 4, (char **)azArg, 0); - if( rc!=SQLITE_OK ){ - sqlite3SafetyOn(db); - return rc; - } - pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName); - if( pTab ){ - pTab->readOnly = 1; - } - sqlite3SafetyOn(db); - - /* Create a cursor to hold the database open - */ - if( db->aDb[iDb].pBt==0 ){ - if( iDb==1 ) DbSetProperty(db, 1, DB_SchemaLoaded); - return SQLITE_OK; - } - rc = sqlite3BtreeCursor(db->aDb[iDb].pBt, MASTER_ROOT, 0, 0, 0, &curMain); - if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){ - sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0); - return rc; - } - - /* Get the database meta information. - ** - ** Meta values are as follows: - ** meta[0] Schema cookie. Changes with each schema change. - ** meta[1] File format of schema layer. - ** meta[2] Size of the page cache. - ** meta[3] Use freelist if 0. Autovacuum if greater than zero. - ** meta[4] Db text encoding. 1:UTF-8 3:UTF-16 LE 4:UTF-16 BE - ** meta[5] - ** meta[6] - ** meta[7] - ** meta[8] - ** meta[9] - ** - ** Note: The hash defined SQLITE_UTF* symbols in sqliteInt.h correspond to - ** the possible values of meta[4]. - */ - if( rc==SQLITE_OK ){ - int i; - for(i=0; rc==SQLITE_OK && i<sizeof(meta)/sizeof(meta[0]); i++){ - rc = sqlite3BtreeGetMeta(db->aDb[iDb].pBt, i+1, (u32 *)&meta[i]); - } - if( rc ){ - sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0); - sqlite3BtreeCloseCursor(curMain); - return rc; - } - }else{ - memset(meta, 0, sizeof(meta)); - } - db->aDb[iDb].schema_cookie = meta[0]; - - /* If opening a non-empty database, check the text encoding. For the - ** main database, set sqlite3.enc to the encoding of the main database. - ** For an attached db, it is an error if the encoding is not the same - ** as sqlite3.enc. - */ - if( meta[4] ){ /* text encoding */ - if( iDb==0 ){ - /* If opening the main database, set db->enc. */ - db->enc = (u8)meta[4]; - db->pDfltColl = sqlite3FindCollSeq(db, db->enc, "BINARY", 6, 0); - }else{ - /* If opening an attached database, the encoding much match db->enc */ - if( meta[4]!=db->enc ){ - sqlite3BtreeCloseCursor(curMain); - sqlite3SetString(pzErrMsg, "attached databases must use the same" - " text encoding as main database", (char*)0); - return SQLITE_ERROR; - } - } - } - - size = meta[2]; - if( size==0 ){ size = MAX_PAGES; } - db->aDb[iDb].cache_size = size; - - if( iDb==0 ){ - db->file_format = meta[1]; - if( db->file_format==0 ){ - /* This happens if the database was initially empty */ - db->file_format = 1; - } - } - - /* - ** file_format==1 Version 3.0.0. - */ - if( meta[1]>1 ){ - sqlite3BtreeCloseCursor(curMain); - sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0); - return SQLITE_ERROR; - } - - sqlite3BtreeSetCacheSize(db->aDb[iDb].pBt, db->aDb[iDb].cache_size); - - /* Read the schema information out of the schema tables - */ - assert( db->init.busy ); - if( rc==SQLITE_EMPTY ){ - /* For an empty database, there is nothing to read */ - rc = SQLITE_OK; - }else{ - char *zSql; - zSql = sqlite3MPrintf( - "SELECT name, rootpage, sql, %s FROM '%q'.%s", - zDbNum, db->aDb[iDb].zName, zMasterName); - sqlite3SafetyOff(db); - rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); - sqlite3SafetyOn(db); - sqliteFree(zSql); - sqlite3BtreeCloseCursor(curMain); - } - if( sqlite3_malloc_failed ){ - sqlite3SetString(pzErrMsg, "out of memory", (char*)0); - rc = SQLITE_NOMEM; - sqlite3ResetInternalSchema(db, 0); - } - if( rc==SQLITE_OK ){ - DbSetProperty(db, iDb, DB_SchemaLoaded); - }else{ - sqlite3ResetInternalSchema(db, iDb); - } - return rc; -} - -/* -** Initialize all database files - the main database file, the file -** used to store temporary tables, and any additional database files -** created using ATTACH statements. Return a success code. If an -** error occurs, write an error message into *pzErrMsg. -** -** After the database is initialized, the SQLITE_Initialized -** bit is set in the flags field of the sqlite structure. -*/ -int sqlite3Init(sqlite3 *db, char **pzErrMsg){ - int i, rc; - - if( db->init.busy ) return SQLITE_OK; - assert( (db->flags & SQLITE_Initialized)==0 ); - rc = SQLITE_OK; - db->init.busy = 1; - for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ - if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; - rc = sqlite3InitOne(db, i, pzErrMsg); - if( rc ){ - sqlite3ResetInternalSchema(db, i); - } - } - - /* Once all the other databases have been initialised, load the schema - ** for the TEMP database. This is loaded last, as the TEMP database - ** schema may contain references to objects in other databases. - */ - if( rc==SQLITE_OK && db->nDb>1 && !DbHasProperty(db, 1, DB_SchemaLoaded) ){ - rc = sqlite3InitOne(db, 1, pzErrMsg); - if( rc ){ - sqlite3ResetInternalSchema(db, 1); - } - } - - db->init.busy = 0; - if( rc==SQLITE_OK ){ - db->flags |= SQLITE_Initialized; - sqlite3CommitInternalChanges(db); - } - - if( rc!=SQLITE_OK ){ - db->flags &= ~SQLITE_Initialized; - } - return rc; -} - -/* -** This routine is a no-op if the database schema is already initialised. -** Otherwise, the schema is loaded. An error code is returned. -*/ -int sqlite3ReadSchema(Parse *pParse){ - int rc = SQLITE_OK; - sqlite3 *db = pParse->db; - if( !db->init.busy ){ - if( (db->flags & SQLITE_Initialized)==0 ){ - rc = sqlite3Init(db, &pParse->zErrMsg); - } - } - assert( rc!=SQLITE_OK || (db->flags & SQLITE_Initialized)||db->init.busy ); - if( rc!=SQLITE_OK ){ - pParse->rc = rc; - pParse->nErr++; - } - return rc; -} - -/* -** The version of the library -*/ -const char rcsid3[] = "@(#) \044Id: SQLite version " SQLITE_VERSION " $"; -const char sqlite3_version[] = SQLITE_VERSION; -const char *sqlite3_libversion(void){ return sqlite3_version; } - -/* -** This is the default collating function named "BINARY" which is always -** available. -*/ -static int binaryCollatingFunc( - void *NotUsed, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - int rc, n; - n = nKey1<nKey2 ? nKey1 : nKey2; - rc = memcmp(pKey1, pKey2, n); - if( rc==0 ){ - rc = nKey1 - nKey2; - } - return rc; -} - -/* -** Another built-in collating sequence: NOCASE. -** -** This collating sequence is intended to be used for "case independant -** comparison". SQLite's knowledge of upper and lower case equivalents -** extends only to the 26 characters used in the English language. -** -** At the moment there is only a UTF-8 implementation. -*/ -static int nocaseCollatingFunc( - void *NotUsed, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - int r = sqlite3StrNICmp( - (const char *)pKey1, (const char *)pKey2, (nKey1<nKey2)?nKey1:nKey2); - if( 0==r ){ - r = nKey1-nKey2; - } - return r; -} - -/* -** Return the ROWID of the most recent insert -*/ -sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ - return db->lastRowid; -} - -/* -** Return the number of changes in the most recent call to sqlite3_exec(). -*/ -int sqlite3_changes(sqlite3 *db){ - return db->nChange; -} - -/* -** Return the number of changes since the database handle was opened. -*/ -int sqlite3_total_changes(sqlite3 *db){ - return db->nTotalChange; -} - -/* -** Close an existing SQLite database -*/ -int sqlite3_close(sqlite3 *db){ - HashElem *i; - int j; - - if( !db ){ - return SQLITE_OK; - } - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - - /* If there are any outstanding VMs, return SQLITE_BUSY. */ - if( db->pVdbe ){ - sqlite3Error(db, SQLITE_BUSY, - "Unable to close due to unfinalised statements"); - return SQLITE_BUSY; - } - assert( !sqlite3SafetyCheck(db) ); - - /* FIX ME: db->magic may be set to SQLITE_MAGIC_CLOSED if the database - ** cannot be opened for some reason. So this routine needs to run in - ** that case. But maybe there should be an extra magic value for the - ** "failed to open" state. - */ - if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){ - /* printf("DID NOT CLOSE\n"); fflush(stdout); */ - return SQLITE_ERROR; - } - - for(j=0; j<db->nDb; j++){ - struct Db *pDb = &db->aDb[j]; - if( pDb->pBt ){ - sqlite3BtreeClose(pDb->pBt); - pDb->pBt = 0; - } - } - sqlite3ResetInternalSchema(db, 0); - assert( db->nDb<=2 ); - assert( db->aDb==db->aDbStatic ); - for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ - FuncDef *pFunc, *pNext; - for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){ - pNext = pFunc->pNext; - sqliteFree(pFunc); - } - } - - for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ - CollSeq *pColl = (CollSeq *)sqliteHashData(i); - sqliteFree(pColl); - } - sqlite3HashClear(&db->aCollSeq); - - sqlite3HashClear(&db->aFunc); - sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ - if( db->pValue ){ - sqlite3ValueFree(db->pValue); - } - if( db->pErr ){ - sqlite3ValueFree(db->pErr); - } - - db->magic = SQLITE_MAGIC_ERROR; - sqliteFree(db); - return SQLITE_OK; -} - -/* -** Rollback all database files. -*/ -void sqlite3RollbackAll(sqlite3 *db){ - int i; - for(i=0; i<db->nDb; i++){ - if( db->aDb[i].pBt ){ - sqlite3BtreeRollback(db->aDb[i].pBt); - db->aDb[i].inTrans = 0; - } - } - sqlite3ResetInternalSchema(db, 0); -} - -/* -** Return a static string that describes the kind of error specified in the -** argument. -*/ -const char *sqlite3ErrStr(int rc){ - const char *z; - switch( rc ){ - case SQLITE_ROW: - case SQLITE_DONE: - case SQLITE_OK: z = "not an error"; break; - case SQLITE_ERROR: z = "SQL logic error or missing database"; break; - case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break; - case SQLITE_PERM: z = "access permission denied"; break; - case SQLITE_ABORT: z = "callback requested query abort"; break; - case SQLITE_BUSY: z = "database is locked"; break; - case SQLITE_LOCKED: z = "database table is locked"; break; - case SQLITE_NOMEM: z = "out of memory"; break; - case SQLITE_READONLY: z = "attempt to write a readonly database"; break; - case SQLITE_INTERRUPT: z = "interrupted"; break; - case SQLITE_IOERR: z = "disk I/O error"; break; - case SQLITE_CORRUPT: z = "database disk image is malformed"; break; - case SQLITE_NOTFOUND: z = "table or record not found"; break; - case SQLITE_FULL: z = "database is full"; break; - case SQLITE_CANTOPEN: z = "unable to open database file"; break; - case SQLITE_PROTOCOL: z = "database locking protocol failure"; break; - case SQLITE_EMPTY: z = "table contains no data"; break; - case SQLITE_SCHEMA: z = "database schema has changed"; break; - case SQLITE_TOOBIG: z = "too much data for one table row"; break; - case SQLITE_CONSTRAINT: z = "constraint failed"; break; - case SQLITE_MISMATCH: z = "datatype mismatch"; break; - case SQLITE_MISUSE: z = "library routine called out of sequence";break; - case SQLITE_NOLFS: z = "kernel lacks large file support"; break; - case SQLITE_AUTH: z = "authorization denied"; break; - case SQLITE_FORMAT: z = "auxiliary database format error"; break; - case SQLITE_RANGE: z = "bind index out of range"; break; - case SQLITE_NOTADB: z = "file is encrypted or is not a database";break; - default: z = "unknown error"; break; - } - return z; -} - -/* -** This routine implements a busy callback that sleeps and tries -** again until a timeout value is reached. The timeout value is -** an integer number of milliseconds passed in as the first -** argument. -*/ -static int sqliteDefaultBusyCallback( - void *Timeout, /* Maximum amount of time to wait */ - int count /* Number of times table has been busy */ -){ -#if SQLITE_MIN_SLEEP_MS==1 - static const char delays[] = - { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 50, 100}; - static const short int totals[] = - { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228, 287}; -# define NDELAY (sizeof(delays)/sizeof(delays[0])) - ptr timeout = (ptr)Timeout; - ptr delay, prior; - - if( count <= NDELAY ){ - delay = delays[count-1]; - prior = totals[count-1]; - }else{ - delay = delays[NDELAY-1]; - prior = totals[NDELAY-1] + delay*(count-NDELAY-1); - } - if( prior + delay > timeout ){ - delay = timeout - prior; - if( delay<=0 ) return 0; - } - sqlite3OsSleep(delay); - return 1; -#else - int timeout = (int)Timeout; - if( (count+1)*1000 > timeout ){ - return 0; - } - sqlite3OsSleep(1000); - return 1; -#endif -} - -/* -** This routine sets the busy callback for an Sqlite database to the -** given callback function with the given argument. -*/ -int sqlite3_busy_handler( - sqlite3 *db, - int (*xBusy)(void*,int), - void *pArg -){ - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - db->busyHandler.xFunc = xBusy; - db->busyHandler.pArg = pArg; - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -/* -** This routine sets the progress callback for an Sqlite database to the -** given callback function with the given argument. The progress callback will -** be invoked every nOps opcodes. -*/ -void sqlite3_progress_handler( - sqlite3 *db, - int nOps, - int (*xProgress)(void*), - void *pArg -){ - if( !sqlite3SafetyCheck(db) ){ - if( nOps>0 ){ - db->xProgress = xProgress; - db->nProgressOps = nOps; - db->pProgressArg = pArg; - }else{ - db->xProgress = 0; - db->nProgressOps = 0; - db->pProgressArg = 0; - } - } -} -#endif - - -/* -** This routine installs a default busy handler that waits for the -** specified number of milliseconds before returning 0. -*/ -int sqlite3_busy_timeout(sqlite3 *db, int ms){ - if( ms>0 ){ - sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)(ptr)ms); - }else{ - sqlite3_busy_handler(db, 0, 0); - } - return SQLITE_OK; -} - -/* -** Cause any pending operation to stop at its earliest opportunity. -*/ -void sqlite3_interrupt(sqlite3 *db){ - if( !sqlite3SafetyCheck(db) ){ - db->flags |= SQLITE_Interrupt; - } -} - -/* -** Windows systems should call this routine to free memory that -** is returned in the in the errmsg parameter of sqlite3_open() when -** SQLite is a DLL. For some reason, it does not work to call free() -** directly. -** -** Note that we need to call free() not sqliteFree() here. -*/ -void sqlite3_free(char *p){ free(p); } - -/* -** Create new user functions. -*/ -int sqlite3_create_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int enc, - void *pUserData, - void (*xFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), - void (*xFinal)(sqlite3_context*) -){ - FuncDef *p; - int nName; - - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - if( zFunctionName==0 || - (xFunc && (xFinal || xStep)) || - (!xFunc && (xFinal && !xStep)) || - (!xFunc && (!xFinal && xStep)) || - (nArg<-1 || nArg>127) || - (255<(nName = strlen(zFunctionName))) ){ - return SQLITE_ERROR; - } - - /* If SQLITE_UTF16 is specified as the encoding type, transform this - ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the - ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. - ** - ** If SQLITE_ANY is specified, add three versions of the function - ** to the hash table. - */ - if( enc==SQLITE_UTF16 ){ - enc = SQLITE_UTF16NATIVE; - }else if( enc==SQLITE_ANY ){ - int rc; - rc = sqlite3_create_function(db, zFunctionName, nArg, SQLITE_UTF8, - pUserData, xFunc, xStep, xFinal); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3_create_function(db, zFunctionName, nArg, SQLITE_UTF16LE, - pUserData, xFunc, xStep, xFinal); - if( rc!=SQLITE_OK ) return rc; - enc = SQLITE_UTF16BE; - } - - p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 1); - if( p==0 ) return SQLITE_NOMEM; - p->xFunc = xFunc; - p->xStep = xStep; - p->xFinalize = xFinal; - p->pUserData = pUserData; - return SQLITE_OK; -} -int sqlite3_create_function16( - sqlite3 *db, - const void *zFunctionName, - int nArg, - int eTextRep, - void *pUserData, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -){ - int rc; - char const *zFunc8; - sqlite3_value *pTmp; - - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - pTmp = sqlite3GetTransientValue(db); - sqlite3ValueSetStr(pTmp, -1, zFunctionName, SQLITE_UTF16NATIVE,SQLITE_STATIC); - zFunc8 = sqlite3ValueText(pTmp, SQLITE_UTF8); - - if( !zFunc8 ){ - return SQLITE_NOMEM; - } - rc = sqlite3_create_function(db, zFunc8, nArg, eTextRep, - pUserData, xFunc, xStep, xFinal); - return rc; -} - -/* -** Register a trace function. The pArg from the previously registered trace -** is returned. -** -** A NULL trace function means that no tracing is executes. A non-NULL -** trace is a pointer to a function that is invoked at the start of each -** sqlite3_exec(). -*/ -void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ - void *pOld = db->pTraceArg; - db->xTrace = xTrace; - db->pTraceArg = pArg; - return pOld; -} - -/*** EXPERIMENTAL *** -** -** Register a function to be invoked when a transaction comments. -** If either function returns non-zero, then the commit becomes a -** rollback. -*/ -void *sqlite3_commit_hook( - sqlite3 *db, /* Attach the hook to this database */ - int (*xCallback)(void*), /* Function to invoke on each commit */ - void *pArg /* Argument to the function */ -){ - void *pOld = db->pCommitArg; - db->xCommitCallback = xCallback; - db->pCommitArg = pArg; - return pOld; -} - - -/* -** This routine is called to create a connection to a database BTree -** driver. If zFilename is the name of a file, then that file is -** opened and used. If zFilename is the magic name ":memory:" then -** the database is stored in memory (and is thus forgotten as soon as -** the connection is closed.) If zFilename is NULL then the database -** is for temporary use only and is deleted as soon as the connection -** is closed. -** -** A temporary database can be either a disk file (that is automatically -** deleted when the file is closed) or a set of red-black trees held in memory, -** depending on the values of the TEMP_STORE compile-time macro and the -** db->temp_store variable, according to the following chart: -** -** TEMP_STORE db->temp_store Location of temporary database -** ---------- -------------- ------------------------------ -** 0 any file -** 1 1 file -** 1 2 memory -** 1 0 file -** 2 1 file -** 2 2 memory -** 2 0 memory -** 3 any memory -*/ -int sqlite3BtreeFactory( - const sqlite3 *db, /* Main database when opening aux otherwise 0 */ - const char *zFilename, /* Name of the file containing the BTree database */ - int omitJournal, /* if TRUE then do not journal this file */ - int nCache, /* How many pages in the page cache */ - Btree **ppBtree /* Pointer to new Btree object written here */ -){ - int btree_flags = 0; - int rc; - - assert( ppBtree != 0); - if( omitJournal ){ - btree_flags |= BTREE_OMIT_JOURNAL; - } - if( zFilename==0 ){ -#ifndef TEMP_STORE -# define TEMP_STORE 1 -#endif -#if TEMP_STORE==0 - /* Do nothing */ -#endif -#if TEMP_STORE==1 - if( db->temp_store==2 ) zFilename = ":memory:"; -#endif -#if TEMP_STORE==2 - if( db->temp_store!=1 ) zFilename = ":memory:"; -#endif -#if TEMP_STORE==3 - zFilename = ":memory:"; -#endif - } - - rc = sqlite3BtreeOpen(zFilename, ppBtree, btree_flags); - if( rc==SQLITE_OK ){ - sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler); - sqlite3BtreeSetCacheSize(*ppBtree, nCache); - } - return rc; -} - -/* -** Return UTF-8 encoded English language explanation of the most recent -** error. -*/ -const char *sqlite3_errmsg(sqlite3 *db){ - const char *z; - if( sqlite3_malloc_failed ){ - return sqlite3ErrStr(SQLITE_NOMEM); - } - if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){ - return sqlite3ErrStr(SQLITE_MISUSE); - } - z = sqlite3_value_text(db->pErr); - if( z==0 ){ - z = sqlite3ErrStr(db->errCode); - } - return z; -} - -/* -** Return UTF-16 encoded English language explanation of the most recent -** error. -*/ -const void *sqlite3_errmsg16(sqlite3 *db){ - /* Because all the characters in the string are in the unicode - ** range 0x00-0xFF, if we pad the big-endian string with a - ** zero byte, we can obtain the little-endian string with - ** &big_endian[1]. - */ - static const char outOfMemBe[] = { - 0, 'o', 0, 'u', 0, 't', 0, ' ', - 0, 'o', 0, 'f', 0, ' ', - 0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0 - }; - static const char misuseBe [] = { - 0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ', - 0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ', - 0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ', - 0, 'o', 0, 'u', 0, 't', 0, ' ', - 0, 'o', 0, 'f', 0, ' ', - 0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0 - }; - - const void *z; - if( sqlite3_malloc_failed ){ - return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); - } - if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){ - return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); - } - z = sqlite3_value_text16(db->pErr); - if( z==0 ){ - sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), - SQLITE_UTF8, SQLITE_STATIC); - z = sqlite3_value_text16(db->pErr); - } - return z; -} - -/* -** Return the most recent error code generated by an SQLite routine. -*/ -int sqlite3_errcode(sqlite3 *db){ - if( sqlite3_malloc_failed ){ - return SQLITE_NOMEM; - } - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - return db->errCode; -} - -/* -** Check schema cookies in all databases. If any cookie is out -** of date, return 0. If all schema cookies are current, return 1. -*/ -static int schemaIsValid(sqlite3 *db){ - int iDb; - int rc; - BtCursor *curTemp; - int cookie; - int allOk = 1; - - for(iDb=0; allOk && iDb<db->nDb; iDb++){ - Btree *pBt; - pBt = db->aDb[iDb].pBt; - if( pBt==0 ) continue; - rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie); - if( rc==SQLITE_OK && cookie!=db->aDb[iDb].schema_cookie ){ - allOk = 0; - } - sqlite3BtreeCloseCursor(curTemp); - } - } - return allOk; -} - -/* -** Compile the UTF-8 encoded SQL statement zSql into a statement handle. -*/ -int sqlite3_prepare( - sqlite3 *db, /* Database handle. */ - const char *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char** pzTail /* OUT: End of parsed string */ -){ - Parse sParse; - char *zErrMsg = 0; - int rc = SQLITE_OK; - - if( sqlite3_malloc_failed ){ - return SQLITE_NOMEM; - } - - assert( ppStmt ); - *ppStmt = 0; - if( sqlite3SafetyOn(db) ){ - return SQLITE_MISUSE; - } - - memset(&sParse, 0, sizeof(sParse)); - sParse.db = db; - sqlite3RunParser(&sParse, zSql, &zErrMsg); - - if( sqlite3_malloc_failed ){ - rc = SQLITE_NOMEM; - sqlite3RollbackAll(db); - sqlite3ResetInternalSchema(db, 0); - db->flags &= ~SQLITE_InTrans; - goto prepare_out; - } - if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; - if( sParse.rc!=SQLITE_OK && sParse.checkSchema && !schemaIsValid(db) ){ - sParse.rc = SQLITE_SCHEMA; - } - if( sParse.rc==SQLITE_SCHEMA ){ - sqlite3ResetInternalSchema(db, 0); - } - if( pzTail ) *pzTail = sParse.zTail; - rc = sParse.rc; - - if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){ - sqlite3VdbeSetNumCols(sParse.pVdbe, 5); - sqlite3VdbeSetColName(sParse.pVdbe, 0, "addr", P3_STATIC); - sqlite3VdbeSetColName(sParse.pVdbe, 1, "opcode", P3_STATIC); - sqlite3VdbeSetColName(sParse.pVdbe, 2, "p1", P3_STATIC); - sqlite3VdbeSetColName(sParse.pVdbe, 3, "p2", P3_STATIC); - sqlite3VdbeSetColName(sParse.pVdbe, 4, "p3", P3_STATIC); - } - -prepare_out: - if( sqlite3SafetyOff(db) ){ - rc = SQLITE_MISUSE; - } - if( rc==SQLITE_OK ){ - *ppStmt = (sqlite3_stmt*)sParse.pVdbe; - }else if( sParse.pVdbe ){ - sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe); - } - - if( zErrMsg ){ - sqlite3Error(db, rc, "%s", zErrMsg); - sqliteFree(zErrMsg); - }else{ - sqlite3Error(db, rc, 0); - } - return rc; -} - -/* -** Compile the UTF-16 encoded SQL statement zSql into a statement handle. -*/ -int sqlite3_prepare16( - sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const void **pzTail /* OUT: End of parsed string */ -){ - /* This function currently works by first transforming the UTF-16 - ** encoded string to UTF-8, then invoking sqlite3_prepare(). The - ** tricky bit is figuring out the pointer to return in *pzTail. - */ - char const *zSql8 = 0; - char const *zTail8 = 0; - int rc; - sqlite3_value *pTmp; - - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - pTmp = sqlite3GetTransientValue(db); - sqlite3ValueSetStr(pTmp, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zSql8 = sqlite3ValueText(pTmp, SQLITE_UTF8); - if( !zSql8 ){ - sqlite3Error(db, SQLITE_NOMEM, 0); - return SQLITE_NOMEM; - } - rc = sqlite3_prepare(db, zSql8, -1, ppStmt, &zTail8); - - if( zTail8 && pzTail ){ - /* If sqlite3_prepare returns a tail pointer, we calculate the - ** equivalent pointer into the UTF-16 string by counting the unicode - ** characters between zSql8 and zTail8, and then returning a pointer - ** the same number of characters into the UTF-16 string. - */ - int chars_parsed = sqlite3utf8CharLen(zSql8, zTail8-zSql8); - *pzTail = (u8 *)zSql + sqlite3utf16ByteLen(zSql, chars_parsed); - } - - return rc; -} - -/* -** This routine does the work of opening a database on behalf of -** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" -** is UTF-8 encoded. The fourth argument, "def_enc" is one of the TEXT_* -** macros from sqliteInt.h. If we end up creating a new database file -** (not opening an existing one), the text encoding of the database -** will be set to this value. -*/ -static int openDatabase( - const char *zFilename, /* Database filename UTF-8 encoded */ - sqlite3 **ppDb /* OUT: Returned database handle */ -){ - sqlite3 *db; - int rc, i; - char *zErrMsg = 0; - - /* Allocate the sqlite data structure */ - db = sqliteMalloc( sizeof(sqlite3) ); - if( db==0 ) goto opendb_out; - db->priorNewRowid = 0; - db->magic = SQLITE_MAGIC_BUSY; - db->nDb = 2; - db->aDb = db->aDbStatic; - db->enc = SQLITE_UTF8; - db->autoCommit = 1; - /* db->flags |= SQLITE_ShortColNames; */ - sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0); - for(i=0; i<db->nDb; i++){ - sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0); - sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1); - } - - /* Add the default collation sequence BINARY. BINARY works for both UTF-8 - ** and UTF-16, so add a version for each to avoid any unnecessary - ** conversions. The only error that can occur here is a malloc() failure. - */ - sqlite3_create_collation(db, "BINARY", SQLITE_UTF8, 0,binaryCollatingFunc); - sqlite3_create_collation(db, "BINARY", SQLITE_UTF16LE, 0,binaryCollatingFunc); - sqlite3_create_collation(db, "BINARY", SQLITE_UTF16BE, 0,binaryCollatingFunc); - db->pDfltColl = sqlite3FindCollSeq(db, db->enc, "BINARY", 6, 0); - if( !db->pDfltColl ){ - rc = db->errCode; - assert( rc!=SQLITE_OK ); - db->magic = SQLITE_MAGIC_CLOSED; - goto opendb_out; - } - - /* Also add a UTF-8 case-insensitive collation sequence. */ - sqlite3_create_collation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc); - - /* Open the backend database driver */ - rc = sqlite3BtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt); - if( rc!=SQLITE_OK ){ - sqlite3Error(db, rc, 0); - db->magic = SQLITE_MAGIC_CLOSED; - goto opendb_out; - } - db->aDb[0].zName = "main"; - db->aDb[1].zName = "temp"; - - /* The default safety_level for the main database is 'full' for the temp - ** database it is 'NONE'. This matches the pager layer defaults. */ - db->aDb[0].safety_level = 3; - db->aDb[1].safety_level = 1; - - /* Register all built-in functions, but do not attempt to read the - ** database schema yet. This is delayed until the first time the database - ** is accessed. - */ - sqlite3RegisterBuiltinFunctions(db); - if( rc==SQLITE_OK ){ - sqlite3Error(db, SQLITE_OK, 0); - db->magic = SQLITE_MAGIC_OPEN; - }else{ - sqlite3Error(db, rc, "%s", zErrMsg, 0); - if( zErrMsg ) sqliteFree(zErrMsg); - db->magic = SQLITE_MAGIC_CLOSED; - } - -opendb_out: - if( sqlite3_errcode(db)==SQLITE_OK && sqlite3_malloc_failed ){ - sqlite3Error(db, SQLITE_NOMEM, 0); - } - *ppDb = db; - return sqlite3_errcode(db); -} - -/* -** Open a new database handle. -*/ -int sqlite3_open( - const char *zFilename, - sqlite3 **ppDb -){ - return openDatabase(zFilename, ppDb); -} - -/* -** Open a new database handle. -*/ -int sqlite3_open16( - const void *zFilename, - sqlite3 **ppDb -){ - char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */ - int rc = SQLITE_NOMEM; - sqlite3_value *pVal; - - assert( ppDb ); - *ppDb = 0; - pVal = sqlite3ValueNew(); - sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8); - if( zFilename8 ){ - rc = openDatabase(zFilename8, ppDb); - if( rc==SQLITE_OK && *ppDb ){ - sqlite3_exec(*ppDb, "PRAGMA encoding = 'UTF-16'", 0, 0, 0); - } - } - if( pVal ){ - sqlite3ValueFree(pVal); - } - - return rc; -} - -/* -** The following routine destroys a virtual machine that is created by -** the sqlite3_compile() routine. The integer returned is an SQLITE_ -** success/failure code that describes the result of executing the virtual -** machine. -** -** This routine sets the error code and string returned by -** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). -*/ -int sqlite3_finalize(sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - rc = SQLITE_OK; - }else{ - rc = sqlite3VdbeFinalize((Vdbe*)pStmt); - } - return rc; -} - -/* -** Terminate the current execution of an SQL statement and reset it -** back to its starting state so that it can be reused. A success code from -** the prior execution is returned. -** -** This routine sets the error code and string returned by -** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). -*/ -int sqlite3_reset(sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - rc = SQLITE_OK; - }else{ - rc = sqlite3VdbeReset((Vdbe*)pStmt); - sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0); - } - return rc; -} - -/* -** Register a new collation sequence with the database handle db. -*/ -int sqlite3_create_collation( - sqlite3* db, - const char *zName, - int enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*) -){ - CollSeq *pColl; - int rc = SQLITE_OK; - - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - - /* If SQLITE_UTF16 is specified as the encoding type, transform this - ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the - ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. - */ - if( enc==SQLITE_UTF16 ){ - enc = SQLITE_UTF16NATIVE; - } - - if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF16LE && enc!=SQLITE_UTF16BE ){ - sqlite3Error(db, SQLITE_ERROR, - "Param 3 to sqlite3_create_collation() must be one of " - "SQLITE_UTF8, SQLITE_UTF16, SQLITE_UTF16LE or SQLITE_UTF16BE" - ); - return SQLITE_ERROR; - } - pColl = sqlite3FindCollSeq(db, (u8)enc, zName, strlen(zName), 1); - if( 0==pColl ){ - rc = SQLITE_NOMEM; - }else{ - pColl->xCmp = xCompare; - pColl->pUser = pCtx; - pColl->enc = enc; - } - sqlite3Error(db, rc, 0); - return rc; -} - -/* -** Register a new collation sequence with the database handle db. -*/ -int sqlite3_create_collation16( - sqlite3* db, - const char *zName, - int enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*) -){ - char const *zName8; - sqlite3_value *pTmp; - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - pTmp = sqlite3GetTransientValue(db); - sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zName8 = sqlite3ValueText(pTmp, SQLITE_UTF8); - return sqlite3_create_collation(db, zName8, enc, pCtx, xCompare); -} - -/* -** Register a collation sequence factory callback with the database handle -** db. Replace any previously installed collation sequence factory. -*/ -int sqlite3_collation_needed( - sqlite3 *db, - void *pCollNeededArg, - void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) -){ - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - db->xCollNeeded = xCollNeeded; - db->xCollNeeded16 = 0; - db->pCollNeededArg = pCollNeededArg; - return SQLITE_OK; -} - -/* -** Register a collation sequence factory callback with the database handle -** db. Replace any previously installed collation sequence factory. -*/ -int sqlite3_collation_needed16( - sqlite3 *db, - void *pCollNeededArg, - void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) -){ - if( sqlite3SafetyCheck(db) ){ - return SQLITE_MISUSE; - } - db->xCollNeeded = 0; - db->xCollNeeded16 = xCollNeeded16; - db->pCollNeededArg = pCollNeededArg; - return SQLITE_OK; -} diff --git a/kopete/plugins/statistics/sqlite/opcodes.c b/kopete/plugins/statistics/sqlite/opcodes.c deleted file mode 100644 index b6f01219..00000000 --- a/kopete/plugins/statistics/sqlite/opcodes.c +++ /dev/null @@ -1,128 +0,0 @@ -/* Automatically generated. Do not edit */ -/* See the mkopcodec.h script for details. */ -const char *const sqlite3OpcodeNames[] = { "?", - "ContextPop", - "IntegrityCk", - "DropTrigger", - "DropIndex", - "Recno", - "KeyAsData", - "Delete", - "MoveGt", - "VerifyCookie", - "Push", - "Dup", - "Blob", - "IdxGT", - "IdxRecno", - "RowKey", - "PutStrKey", - "IsUnique", - "SetNumColumns", - "IdxIsNull", - "NullRow", - "OpenPseudo", - "OpenWrite", - "OpenRead", - "Transaction", - "AutoCommit", - "Pop", - "Halt", - "Vacuum", - "ListRead", - "RowData", - "NotExists", - "MoveLe", - "SetCookie", - "Variable", - "AggNext", - "AggReset", - "Sort", - "IdxDelete", - "ResetCount", - "OpenTemp", - "IdxColumn", - "Integer", - "AggSet", - "CreateIndex", - "IdxPut", - "MoveLt", - "Return", - "MemLoad", - "SortNext", - "IdxLT", - "Rewind", - "AddImm", - "AggFunc", - "AggInit", - "MemIncr", - "ListReset", - "Clear", - "Or", - "And", - "Not", - "PutIntKey", - "If", - "Callback", - "IsNull", - "NotNull", - "Ne", - "Eq", - "Gt", - "Le", - "Lt", - "Ge", - "BitAnd", - "BitOr", - "ShiftLeft", - "ShiftRight", - "Add", - "Subtract", - "Multiply", - "Divide", - "Remainder", - "Concat", - "Negative", - "SortReset", - "BitNot", - "String8", - "SortPut", - "Last", - "NotFound", - "MakeRecord", - "String", - "Goto", - "AggFocus", - "DropTable", - "Column", - "Noop", - "AggGet", - "CreateTable", - "NewRecno", - "Found", - "Distinct", - "Close", - "Statement", - "IfNot", - "Pull", - "MemStore", - "Next", - "Prev", - "MoveGe", - "MustBeInt", - "ForceInt", - "CollSeq", - "Gosub", - "ContextPush", - "ListRewind", - "ListWrite", - "ParseSchema", - "Destroy", - "IdxGE", - "FullKey", - "ReadCookie", - "AbsValue", - "Real", - "HexBlob", - "Function", -}; diff --git a/kopete/plugins/statistics/sqlite/opcodes.h b/kopete/plugins/statistics/sqlite/opcodes.h deleted file mode 100644 index 7b792c5a..00000000 --- a/kopete/plugins/statistics/sqlite/opcodes.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Automatically generated. Do not edit */ -/* See the mkopcodeh.awk script for details */ -#define OP_ContextPop 1 -#define OP_IntegrityCk 2 -#define OP_DropTrigger 3 -#define OP_DropIndex 4 -#define OP_Recno 5 -#define OP_KeyAsData 6 -#define OP_Delete 7 -#define OP_MoveGt 8 -#define OP_VerifyCookie 9 -#define OP_Push 10 -#define OP_Dup 11 -#define OP_Blob 12 -#define OP_IdxGT 13 -#define OP_IdxRecno 14 -#define OP_RowKey 15 -#define OP_PutStrKey 16 -#define OP_IsUnique 17 -#define OP_SetNumColumns 18 -#define OP_Eq 67 -#define OP_IdxIsNull 19 -#define OP_NullRow 20 -#define OP_OpenPseudo 21 -#define OP_OpenWrite 22 -#define OP_OpenRead 23 -#define OP_Transaction 24 -#define OP_AutoCommit 25 -#define OP_Negative 82 -#define OP_Pop 26 -#define OP_Halt 27 -#define OP_Vacuum 28 -#define OP_ListRead 29 -#define OP_RowData 30 -#define OP_NotExists 31 -#define OP_MoveLe 32 -#define OP_SetCookie 33 -#define OP_Variable 34 -#define OP_AggNext 35 -#define OP_AggReset 36 -#define OP_Sort 37 -#define OP_IdxDelete 38 -#define OP_ResetCount 39 -#define OP_OpenTemp 40 -#define OP_IdxColumn 41 -#define OP_NotNull 65 -#define OP_Ge 71 -#define OP_Remainder 80 -#define OP_Divide 79 -#define OP_Integer 42 -#define OP_AggSet 43 -#define OP_CreateIndex 44 -#define OP_IdxPut 45 -#define OP_MoveLt 46 -#define OP_And 59 -#define OP_ShiftLeft 74 -#define OP_Real 122 -#define OP_Return 47 -#define OP_MemLoad 48 -#define OP_SortNext 49 -#define OP_IdxLT 50 -#define OP_Rewind 51 -#define OP_Gt 68 -#define OP_AddImm 52 -#define OP_Subtract 77 -#define OP_AggFunc 53 -#define OP_AggInit 54 -#define OP_MemIncr 55 -#define OP_ListReset 56 -#define OP_Clear 57 -#define OP_PutIntKey 61 -#define OP_IsNull 64 -#define OP_If 62 -#define OP_Callback 63 -#define OP_SortReset 83 -#define OP_SortPut 86 -#define OP_Last 87 -#define OP_NotFound 88 -#define OP_MakeRecord 89 -#define OP_BitAnd 72 -#define OP_Add 76 -#define OP_HexBlob 123 -#define OP_String 90 -#define OP_Goto 91 -#define OP_AggFocus 92 -#define OP_DropTable 93 -#define OP_Column 94 -#define OP_Noop 95 -#define OP_Not 60 -#define OP_Le 69 -#define OP_BitOr 73 -#define OP_Multiply 78 -#define OP_String8 85 -#define OP_AggGet 96 -#define OP_CreateTable 97 -#define OP_NewRecno 98 -#define OP_Found 99 -#define OP_Distinct 100 -#define OP_Close 101 -#define OP_Statement 102 -#define OP_IfNot 103 -#define OP_Pull 104 -#define OP_MemStore 105 -#define OP_Next 106 -#define OP_Prev 107 -#define OP_MoveGe 108 -#define OP_Lt 70 -#define OP_Ne 66 -#define OP_MustBeInt 109 -#define OP_ForceInt 110 -#define OP_ShiftRight 75 -#define OP_CollSeq 111 -#define OP_Gosub 112 -#define OP_ContextPush 113 -#define OP_ListRewind 114 -#define OP_ListWrite 115 -#define OP_ParseSchema 116 -#define OP_Destroy 117 -#define OP_IdxGE 118 -#define OP_FullKey 119 -#define OP_ReadCookie 120 -#define OP_BitNot 84 -#define OP_AbsValue 121 -#define OP_Or 58 -#define OP_Function 124 -#define OP_Concat 81 diff --git a/kopete/plugins/statistics/sqlite/os.h b/kopete/plugins/statistics/sqlite/os.h deleted file mode 100644 index fc478baa..00000000 --- a/kopete/plugins/statistics/sqlite/os.h +++ /dev/null @@ -1,197 +0,0 @@ -/* -** 2001 September 16 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file (together with is companion C source-code file -** "os.c") attempt to abstract the underlying operating system so that -** the SQLite library will work on both POSIX and windows systems. -*/ -#ifndef _SQLITE_OS_H_ -#define _SQLITE_OS_H_ - -/* -** Figure out if we are dealing with Unix, Windows or MacOS. -** -** N.B. MacOS means Mac Classic (or Carbon). Treat Darwin (OS X) as Unix. -** The MacOS build is designed to use CodeWarrior (tested with v8) -*/ -#if !defined(OS_UNIX) && !defined(OS_TEST) -# ifndef OS_WIN -# ifndef OS_MAC -# if defined(__MACOS__) -# define OS_MAC 1 -# define OS_WIN 0 -# define OS_UNIX 0 -# elif defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) -# define OS_MAC 0 -# define OS_WIN 1 -# define OS_UNIX 0 -# else -# define OS_MAC 0 -# define OS_WIN 0 -# define OS_UNIX 1 -# endif -# else -# define OS_WIN 0 -# define OS_UNIX 0 -# endif -# else -# define OS_MAC 0 -# define OS_UNIX 0 -# endif -#else -# define OS_MAC 0 -# ifndef OS_WIN -# define OS_WIN 0 -# endif -#endif - -/* -** Invoke the appropriate operating-system specific header file. -*/ -#if OS_TEST -# include "os_test.h" -#endif -#if OS_UNIX -# include "os_unix.h" -#endif -#if OS_WIN -# include "os_win.h" -#endif -#if OS_MAC -# include "os_mac.h" -#endif - -/* -** Temporary files are named starting with this prefix followed by 16 random -** alphanumeric characters, and no file extension. They are stored in the -** OS's standard temporary file directory, and are deleted prior to exit. -** If sqlite is being embedded in another program, you may wish to change the -** prefix to reflect your program's name, so that if your program exits -** prematurely, old temporary files can be easily identified. This can be done -** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line. -*/ -#ifndef TEMP_FILE_PREFIX -# define TEMP_FILE_PREFIX "sqlite_" -#endif - -/* -** The following values may be passed as the second argument to -** sqlite3OsLock(). The various locks exhibit the following semantics: -** -** SHARED: Any number of processes may hold a SHARED lock simultaneously. -** RESERVED: A single process may hold a RESERVED lock on a file at -** any time. Other processes may hold and obtain new SHARED locks. -** PENDING: A single process may hold a PENDING lock on a file at -** any one time. Existing SHARED locks may persist, but no new -** SHARED locks may be obtained by other processes. -** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. -** -** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a -** process that requests an EXCLUSIVE lock may actually obtain a PENDING -** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to -** sqlite3OsLock(). -*/ -#define NO_LOCK 0 -#define SHARED_LOCK 1 -#define RESERVED_LOCK 2 -#define PENDING_LOCK 3 -#define EXCLUSIVE_LOCK 4 - -/* -** File Locking Notes: (Mostly about windows but also some info for Unix) -** -** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because -** those functions are not available. So we use only LockFile() and -** UnlockFile(). -** -** LockFile() prevents not just writing but also reading by other processes. -** A SHARED_LOCK is obtained by locking a single randomly-chosen -** byte out of a specific range of bytes. The lock byte is obtained at -** random so two separate readers can probably access the file at the -** same time, unless they are unlucky and choose the same lock byte. -** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. -** There can only be one writer. A RESERVED_LOCK is obtained by locking -** a single byte of the file that is designated as the reserved lock byte. -** A PENDING_LOCK is obtained by locking a designated byte different from -** the RESERVED_LOCK byte. -** -** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -** which means we can use reader/writer locks. When reader/writer locks -** are used, the lock is placed on the same range of bytes that is used -** for probabilistic locking in Win95/98/ME. Hence, the locking scheme -** will support two or more Win95 readers or two or more WinNT readers. -** But a single Win95 reader will lock out all WinNT readers and a single -** WinNT reader will lock out all other Win95 readers. -** -** The following #defines specify the range of bytes used for locking. -** SHARED_SIZE is the number of bytes available in the pool from which -** a random byte is selected for a shared lock. The pool of bytes for -** shared locks begins at SHARED_FIRST. -** -** These #defines are available in os.h so that Unix can use the same -** byte ranges for locking. This leaves open the possiblity of having -** clients on win95, winNT, and unix all talking to the same shared file -** and all locking correctly. To do so would require that samba (or whatever -** tool is being used for file sharing) implements locks correctly between -** windows and unix. I'm guessing that isn't likely to happen, but by -** using the same locking range we are at least open to the possibility. -** -** Locking in windows is manditory. For this reason, we cannot store -** actual data in the bytes used for locking. The pager never allocates -** the pages involved in locking therefore. SHARED_SIZE is selected so -** that all locks will fit on a single page even at the minimum page size. -** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE -** is set high so that we don't have to allocate an unused page except -** for very large databases. But one should test the page skipping logic -** by setting PENDING_BYTE low and running the entire regression suite. -** -** Changing the value of PENDING_BYTE results in a subtly incompatible -** file format. Depending on how it is changed, you might not notice -** the incompatibility right away, even running a full regression test. -** The default location of PENDING_BYTE is the first byte past the -** 1GB boundary. -** -*/ -#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */ -/* #define PENDING_BYTE 0x5400 // Page 20 - for testing */ -#define RESERVED_BYTE (PENDING_BYTE+1) -#define SHARED_FIRST (PENDING_BYTE+2) -#define SHARED_SIZE 510 - - -int sqlite3OsDelete(const char*); -int sqlite3OsFileExists(const char*); -int sqlite3OsOpenReadWrite(const char*, OsFile*, int*); -int sqlite3OsOpenExclusive(const char*, OsFile*, int); -int sqlite3OsOpenReadOnly(const char*, OsFile*); -int sqlite3OsOpenDirectory(const char*, OsFile*); -int sqlite3OsSyncDirectory(const char*); -int sqlite3OsTempFileName(char*); -int sqlite3OsClose(OsFile*); -int sqlite3OsRead(OsFile*, void*, int amt); -int sqlite3OsWrite(OsFile*, const void*, int amt); -int sqlite3OsSeek(OsFile*, i64 offset); -int sqlite3OsSync(OsFile*); -int sqlite3OsTruncate(OsFile*, i64 size); -int sqlite3OsFileSize(OsFile*, i64 *pSize); -int sqlite3OsRandomSeed(char*); -int sqlite3OsSleep(int ms); -int sqlite3OsCurrentTime(double*); -int sqlite3OsFileModTime(OsFile*, double*); -void sqlite3OsEnterMutex(void); -void sqlite3OsLeaveMutex(void); -char *sqlite3OsFullPathname(const char*); -int sqlite3OsLock(OsFile*, int); -int sqlite3OsUnlock(OsFile*, int); -int sqlite3OsCheckReservedLock(OsFile *id); - -#endif /* _SQLITE_OS_H_ */ diff --git a/kopete/plugins/statistics/sqlite/os_common.h b/kopete/plugins/statistics/sqlite/os_common.h deleted file mode 100644 index 94311b96..00000000 --- a/kopete/plugins/statistics/sqlite/os_common.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - - -int sqlite3_os_trace = 0; -#ifdef SQLITE_DEBUG -static int last_page = 0; -#define SEEK(X) last_page=(X) -#define TRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X) -#define TRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y) -#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z) -#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A) -#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B) -#define TRACE6(X,Y,Z,A,B,C) if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C) -#define TRACE7(X,Y,Z,A,B,C,D) \ - if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D) -#else -#define SEEK(X) -#define TRACE1(X) -#define TRACE2(X,Y) -#define TRACE3(X,Y,Z) -#define TRACE4(X,Y,Z,A) -#define TRACE5(X,Y,Z,A,B) -#define TRACE6(X,Y,Z,A,B,C) -#define TRACE7(X,Y,Z,A,B,C,D) -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE -__inline__ unsigned long long int hwtime(void){ - unsigned long long int x; - __asm__("rdtsc\n\t" - "mov %%edx, %%ecx\n\t" - :"=A" (x)); - return x; -} -static unsigned long long int g_start; -static unsigned int elapse; -#define TIMER_START g_start=hwtime() -#define TIMER_END elapse=hwtime()-g_start -#define TIMER_ELAPSED elapse -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED 0 -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#ifdef SQLITE_TEST -int sqlite3_io_error_pending = 0; -int sqlite3_diskfull_pending = 0; -#define SimulateIOError(A) \ - if( sqlite3_io_error_pending ) \ - if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); return A; } -static void local_ioerr(){ - sqlite3_io_error_pending = 0; /* Really just a place to set a breakpoint */ -} -#define SimulateDiskfullError \ - if( sqlite3_diskfull_pending ) \ - if( sqlite3_diskfull_pending-- == 1 ){ local_ioerr(); return SQLITE_FULL; } -#else -#define SimulateIOError(A) -#define SimulateDiskfullError -#endif - -/* -** When testing, keep a count of the number of open files. -*/ -#ifdef SQLITE_TEST -int sqlite3_open_file_count = 0; -#define OpenCounter(X) sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif diff --git a/kopete/plugins/statistics/sqlite/os_mac.c b/kopete/plugins/statistics/sqlite/os_mac.c deleted file mode 100644 index f84c168d..00000000 --- a/kopete/plugins/statistics/sqlite/os_mac.c +++ /dev/null @@ -1,738 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains code that is specific classic mac. Mac OS X -** uses the os_unix.c file, not this one. -*/ -#include "sqliteInt.h" -#include "os.h" -#if OS_MAC /* This file used on classic mac only */ - -#include <extras.h> -#include <path2fss.h> -#include <TextUtils.h> -#include <FinderRegistry.h> -#include <Folders.h> -#include <Timer.h> -#include <OSUtils.h> - -/* -** Macros used to determine whether or not to use threads. -*/ -#if defined(THREADSAFE) && THREADSAFE -# include <Multiprocessing.h> -# define SQLITE_MACOS_MULTITASKING 1 -#endif - -/* -** Include code that is common to all os_*.c files -*/ -#include "os_common.h" - -/* -** Delete the named file -*/ -int sqlite3OsDelete(const char *zFilename){ - unlink(zFilename); - return SQLITE_OK; -} - -/* -** Return TRUE if the named file exists. -*/ -int sqlite3OsFileExists(const char *zFilename){ - return access(zFilename, 0)==0; -} - -/* -** Attempt to open a file for both reading and writing. If that -** fails, try opening it read-only. If the file does not exist, -** try to create it. -** -** On success, a handle for the open file is written to *id -** and *pReadonly is set to 0 if the file was opened for reading and -** writing or 1 if the file was opened read-only. The function returns -** SQLITE_OK. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id and *pReadonly unchanged. -*/ -int sqlite3OsOpenReadWrite( - const char *zFilename, - OsFile *id, - int *pReadonly -){ - FSSpec fsSpec; -# ifdef _LARGE_FILE - HFSUniStr255 dfName; - FSRef fsRef; - if( __path2fss(zFilename, &fsSpec) != noErr ){ - if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) - return SQLITE_CANTOPEN; - } - if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) - return SQLITE_CANTOPEN; - FSGetDataForkName(&dfName); - if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, - fsRdWrShPerm, &(id->refNum)) != noErr ){ - if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, - fsRdWrPerm, &(id->refNum)) != noErr ){ - if (FSOpenFork(&fsRef, dfName.length, dfName.unicode, - fsRdPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; - else - *pReadonly = 1; - } else - *pReadonly = 0; - } else - *pReadonly = 0; -# else - __path2fss(zFilename, &fsSpec); - if( !sqlite3OsFileExists(zFilename) ){ - if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) - return SQLITE_CANTOPEN; - } - if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){ - if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){ - if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; - else - *pReadonly = 1; - } else - *pReadonly = 0; - } else - *pReadonly = 0; -# endif - if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ - id->refNumRF = -1; - } - id->locked = 0; - id->delOnClose = 0; - OpenCounter(+1); - return SQLITE_OK; -} - - -/* -** Attempt to open a new file for exclusive access by this process. -** The file will be opened for both reading and writing. To avoid -** a potential security problem, we do not allow the file to have -** previously existed. Nor do we allow the file to be a symbolic -** link. -** -** If delFlag is true, then make arrangements to automatically delete -** the file when it is closed. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ - FSSpec fsSpec; -# ifdef _LARGE_FILE - HFSUniStr255 dfName; - FSRef fsRef; - __path2fss(zFilename, &fsSpec); - if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) - return SQLITE_CANTOPEN; - if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) - return SQLITE_CANTOPEN; - FSGetDataForkName(&dfName); - if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, - fsRdWrPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; -# else - __path2fss(zFilename, &fsSpec); - if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) - return SQLITE_CANTOPEN; - if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; -# endif - id->refNumRF = -1; - id->locked = 0; - id->delOnClose = delFlag; - if (delFlag) - id->pathToDel = sqlite3OsFullPathname(zFilename); - OpenCounter(+1); - return SQLITE_OK; -} - -/* -** Attempt to open a new file for read-only access. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ - FSSpec fsSpec; -# ifdef _LARGE_FILE - HFSUniStr255 dfName; - FSRef fsRef; - if( __path2fss(zFilename, &fsSpec) != noErr ) - return SQLITE_CANTOPEN; - if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) - return SQLITE_CANTOPEN; - FSGetDataForkName(&dfName); - if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, - fsRdPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; -# else - __path2fss(zFilename, &fsSpec); - if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) - return SQLITE_CANTOPEN; -# endif - if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ - id->refNumRF = -1; - } - id->locked = 0; - id->delOnClose = 0; - OpenCounter(+1); - return SQLITE_OK; -} - -/* -** Attempt to open a file descriptor for the directory that contains a -** file. This file descriptor can be used to fsync() the directory -** in order to make sure the creation of a new file is actually written -** to disk. -** -** This routine is only meaningful for Unix. It is a no-op under -** windows since windows does not support hard links. -** -** On success, a handle for a previously open file is at *id is -** updated with the new directory file descriptor and SQLITE_OK is -** returned. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id unchanged. -*/ -int sqlite3OsOpenDirectory( - const char *zDirname, - OsFile *id -){ - return SQLITE_OK; -} - -/* -** Create a temporary file name in zBuf. zBuf must be big enough to -** hold at least SQLITE_TEMPNAME_SIZE characters. -*/ -int sqlite3OsTempFileName(char *zBuf){ - static char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - int i, j; - char zTempPath[SQLITE_TEMPNAME_SIZE]; - char zdirName[32]; - CInfoPBRec infoRec; - Str31 dirName; - memset(&infoRec, 0, sizeof(infoRec)); - memset(zTempPath, 0, SQLITE_TEMPNAME_SIZE); - if( FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, - &(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){ - infoRec.dirInfo.ioNamePtr = dirName; - do{ - infoRec.dirInfo.ioFDirIndex = -1; - infoRec.dirInfo.ioDrDirID = infoRec.dirInfo.ioDrParID; - if( PBGetCatInfoSync(&infoRec) == noErr ){ - CopyPascalStringToC(dirName, zdirName); - i = strlen(zdirName); - memmove(&(zTempPath[i+1]), zTempPath, strlen(zTempPath)); - strcpy(zTempPath, zdirName); - zTempPath[i] = ':'; - }else{ - *zTempPath = 0; - break; - } - } while( infoRec.dirInfo.ioDrDirID != fsRtDirID ); - } - if( *zTempPath == 0 ) - getcwd(zTempPath, SQLITE_TEMPNAME_SIZE-24); - for(;;){ - sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zTempPath); - j = strlen(zBuf); - sqlite3Randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - if( !sqlite3OsFileExists(zBuf) ) break; - } - return SQLITE_OK; -} - -/* -** Close a file. -*/ -int sqlite3OsClose(OsFile *id){ - if( id->refNumRF!=-1 ) - FSClose(id->refNumRF); -# ifdef _LARGE_FILE - FSCloseFork(id->refNum); -# else - FSClose(id->refNum); -# endif - if( id->delOnClose ){ - unlink(id->pathToDel); - sqliteFree(id->pathToDel); - } - OpenCounter(-1); - return SQLITE_OK; -} - -/* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. -*/ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ - int got; - SimulateIOError(SQLITE_IOERR); - TRACE2("READ %d\n", last_page); -# ifdef _LARGE_FILE - FSReadFork(id->refNum, fsAtMark, 0, (ByteCount)amt, pBuf, (ByteCount*)&got); -# else - got = amt; - FSRead(id->refNum, &got, pBuf); -# endif - if( got==amt ){ - return SQLITE_OK; - }else{ - return SQLITE_IOERR; - } -} - -/* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. -*/ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ - OSErr oserr; - int wrote = 0; - SimulateIOError(SQLITE_IOERR); - TRACE2("WRITE %d\n", last_page); - while( amt>0 ){ -# ifdef _LARGE_FILE - oserr = FSWriteFork(id->refNum, fsAtMark, 0, - (ByteCount)amt, pBuf, (ByteCount*)&wrote); -# else - wrote = amt; - oserr = FSWrite(id->refNum, &wrote, pBuf); -# endif - if( wrote == 0 || oserr != noErr) - break; - amt -= wrote; - pBuf = &((char*)pBuf)[wrote]; - } - if( oserr != noErr || amt>wrote ){ - return SQLITE_FULL; - } - return SQLITE_OK; -} - -/* -** Move the read/write pointer in a file. -*/ -int sqlite3OsSeek(OsFile *id, off_t offset){ - off_t curSize; - SEEK(offset/1024 + 1); - if( sqlite3OsFileSize(id, &curSize) != SQLITE_OK ){ - return SQLITE_IOERR; - } - if( offset >= curSize ){ - if( sqlite3OsTruncate(id, offset+1) != SQLITE_OK ){ - return SQLITE_IOERR; - } - } -# ifdef _LARGE_FILE - if( FSSetForkPosition(id->refNum, fsFromStart, offset) != noErr ){ -# else - if( SetFPos(id->refNum, fsFromStart, offset) != noErr ){ -# endif - return SQLITE_IOERR; - }else{ - return SQLITE_OK; - } -} - -/* -** Make sure all writes to a particular file are committed to disk. -** -** Under Unix, also make sure that the directory entry for the file -** has been created by fsync-ing the directory that contains the file. -** If we do not do this and we encounter a power failure, the directory -** entry for the journal might not exist after we reboot. The next -** SQLite to access the file will not know that the journal exists (because -** the directory entry for the journal was never created) and the transaction -** will not roll back - possibly leading to database corruption. -*/ -int sqlite3OsSync(OsFile *id){ -# ifdef _LARGE_FILE - if( FSFlushFork(id->refNum) != noErr ){ -# else - ParamBlockRec params; - memset(¶ms, 0, sizeof(ParamBlockRec)); - params.ioParam.ioRefNum = id->refNum; - if( PBFlushFileSync(¶ms) != noErr ){ -# endif - return SQLITE_IOERR; - }else{ - return SQLITE_OK; - } -} - -/* -** Sync the directory zDirname. This is a no-op on operating systems other -** than UNIX. -*/ -int sqlite3OsSyncDirectory(const char *zDirname){ - SimulateIOError(SQLITE_IOERR); - return SQLITE_OK; -} - -/* -** Truncate an open file to a specified size -*/ -int sqlite3OsTruncate(OsFile *id, off_t nByte){ - SimulateIOError(SQLITE_IOERR); -# ifdef _LARGE_FILE - if( FSSetForkSize(id->refNum, fsFromStart, nByte) != noErr){ -# else - if( SetEOF(id->refNum, nByte) != noErr ){ -# endif - return SQLITE_IOERR; - }else{ - return SQLITE_OK; - } -} - -/* -** Determine the current size of a file in bytes -*/ -int sqlite3OsFileSize(OsFile *id, off_t *pSize){ -# ifdef _LARGE_FILE - if( FSGetForkSize(id->refNum, pSize) != noErr){ -# else - if( GetEOF(id->refNum, pSize) != noErr ){ -# endif - return SQLITE_IOERR; - }else{ - return SQLITE_OK; - } -} - -/* -** Windows file locking notes: [similar issues apply to MacOS] -** -** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because -** those functions are not available. So we use only LockFile() and -** UnlockFile(). -** -** LockFile() prevents not just writing but also reading by other processes. -** (This is a design error on the part of Windows, but there is nothing -** we can do about that.) So the region used for locking is at the -** end of the file where it is unlikely to ever interfere with an -** actual read attempt. -** -** A database read lock is obtained by locking a single randomly-chosen -** byte out of a specific range of bytes. The lock byte is obtained at -** random so two separate readers can probably access the file at the -** same time, unless they are unlucky and choose the same lock byte. -** A database write lock is obtained by locking all bytes in the range. -** There can only be one writer. -** -** A lock is obtained on the first byte of the lock range before acquiring -** either a read lock or a write lock. This prevents two processes from -** attempting to get a lock at a same time. The semantics of -** sqlite3OsReadLock() require that if there is already a write lock, that -** lock is converted into a read lock atomically. The lock on the first -** byte allows us to drop the old write lock and get the read lock without -** another process jumping into the middle and messing us up. The same -** argument applies to sqlite3OsWriteLock(). -** -** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -** which means we can use reader/writer locks. When reader writer locks -** are used, the lock is placed on the same range of bytes that is used -** for probabilistic locking in Win95/98/ME. Hence, the locking scheme -** will support two or more Win95 readers or two or more WinNT readers. -** But a single Win95 reader will lock out all WinNT readers and a single -** WinNT reader will lock out all other Win95 readers. -** -** Note: On MacOS we use the resource fork for locking. -** -** The following #defines specify the range of bytes used for locking. -** N_LOCKBYTE is the number of bytes available for doing the locking. -** The first byte used to hold the lock while the lock is changing does -** not count toward this number. FIRST_LOCKBYTE is the address of -** the first byte in the range of bytes used for locking. -*/ -#define N_LOCKBYTE 10239 -#define FIRST_LOCKBYTE (0x000fffff - N_LOCKBYTE) - -/* -** Change the status of the lock on the file "id" to be a readlock. -** If the file was write locked, then this reduces the lock to a read. -** If the file was read locked, then this acquires a new read lock. -** -** Return SQLITE_OK on success and SQLITE_BUSY on failure. If this -** library was compiled with large file support (LFS) but LFS is not -** available on the host, then an SQLITE_NOLFS is returned. -*/ -int sqlite3OsReadLock(OsFile *id){ - int rc; - if( id->locked>0 || id->refNumRF == -1 ){ - rc = SQLITE_OK; - }else{ - int lk; - OSErr res; - int cnt = 5; - ParamBlockRec params; - sqlite3Randomness(sizeof(lk), &lk); - lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; - memset(¶ms, 0, sizeof(params)); - params.ioParam.ioRefNum = id->refNumRF; - params.ioParam.ioPosMode = fsFromStart; - params.ioParam.ioPosOffset = FIRST_LOCKBYTE; - params.ioParam.ioReqCount = 1; - while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ - UInt32 finalTicks; - Delay(1, &finalTicks); /* 1/60 sec */ - } - if( res == noErr ){ - params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; - params.ioParam.ioReqCount = N_LOCKBYTE; - PBUnlockRangeSync(¶ms); - params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk; - params.ioParam.ioReqCount = 1; - res = PBLockRangeSync(¶ms); - params.ioParam.ioPosOffset = FIRST_LOCKBYTE; - params.ioParam.ioReqCount = 1; - PBUnlockRangeSync(¶ms); - } - if( res == noErr ){ - id->locked = lk; - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; - } - } - return rc; -} - -/* -** Change the lock status to be an exclusive or write lock. Return -** SQLITE_OK on success and SQLITE_BUSY on a failure. If this -** library was compiled with large file support (LFS) but LFS is not -** available on the host, then an SQLITE_NOLFS is returned. -*/ -int sqlite3OsWriteLock(OsFile *id){ - int rc; - if( id->locked<0 || id->refNumRF == -1 ){ - rc = SQLITE_OK; - }else{ - OSErr res; - int cnt = 5; - ParamBlockRec params; - memset(¶ms, 0, sizeof(params)); - params.ioParam.ioRefNum = id->refNumRF; - params.ioParam.ioPosMode = fsFromStart; - params.ioParam.ioPosOffset = FIRST_LOCKBYTE; - params.ioParam.ioReqCount = 1; - while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ - UInt32 finalTicks; - Delay(1, &finalTicks); /* 1/60 sec */ - } - if( res == noErr ){ - params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked; - params.ioParam.ioReqCount = 1; - if( id->locked==0 - || PBUnlockRangeSync(¶ms)==noErr ){ - params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; - params.ioParam.ioReqCount = N_LOCKBYTE; - res = PBLockRangeSync(¶ms); - }else{ - res = afpRangeNotLocked; - } - params.ioParam.ioPosOffset = FIRST_LOCKBYTE; - params.ioParam.ioReqCount = 1; - PBUnlockRangeSync(¶ms); - } - if( res == noErr ){ - id->locked = -1; - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; - } - } - return rc; -} - -/* -** Unlock the given file descriptor. If the file descriptor was -** not previously locked, then this routine is a no-op. If this -** library was compiled with large file support (LFS) but LFS is not -** available on the host, then an SQLITE_NOLFS is returned. -*/ -int sqlite3OsUnlock(OsFile *id){ - int rc; - ParamBlockRec params; - memset(¶ms, 0, sizeof(params)); - params.ioParam.ioRefNum = id->refNumRF; - params.ioParam.ioPosMode = fsFromStart; - if( id->locked==0 || id->refNumRF == -1 ){ - rc = SQLITE_OK; - }else if( id->locked<0 ){ - params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; - params.ioParam.ioReqCount = N_LOCKBYTE; - PBUnlockRangeSync(¶ms); - rc = SQLITE_OK; - id->locked = 0; - }else{ - params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked; - params.ioParam.ioReqCount = 1; - PBUnlockRangeSync(¶ms); - rc = SQLITE_OK; - id->locked = 0; - } - return rc; -} - -/* -** Get information to seed the random number generator. The seed -** is written into the buffer zBuf[256]. The calling function must -** supply a sufficiently large buffer. -*/ -int sqlite3OsRandomSeed(char *zBuf){ - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence.* This makes the - ** tests repeatable. - */ - memset(zBuf, 0, 256); -#if !defined(SQLITE_TEST) - { - int pid; - Microseconds((UnsignedWide*)zBuf); - pid = getpid(); - memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid)); - } -#endif - return SQLITE_OK; -} - -/* -** Sleep for a little while. Return the amount of time slept. -*/ -int sqlite3OsSleep(int ms){ - UInt32 finalTicks; - UInt32 ticks = (((UInt32)ms+16)*3)/50; /* 1/60 sec per tick */ - Delay(ticks, &finalTicks); - return (int)((ticks*50)/3); -} - -/* -** Static variables used for thread synchronization -*/ -static int inMutex = 0; -#ifdef SQLITE_MACOS_MULTITASKING - static MPCriticalRegionID criticalRegion; -#endif - -/* -** The following pair of routine implement mutual exclusion for -** multi-threaded processes. Only a single thread is allowed to -** executed code that is surrounded by EnterMutex() and LeaveMutex(). -** -** SQLite uses only a single Mutex. There is not much critical -** code and what little there is executes quickly and without blocking. -*/ -void sqlite3OsEnterMutex(){ -#ifdef SQLITE_MACOS_MULTITASKING - static volatile int notInit = 1; - if( notInit ){ - if( notInit == 2 ) /* as close as you can get to thread safe init */ - MPYield(); - else{ - notInit = 2; - MPCreateCriticalRegion(&criticalRegion); - notInit = 0; - } - } - MPEnterCriticalRegion(criticalRegion, kDurationForever); -#endif - assert( !inMutex ); - inMutex = 1; -} -void sqlite3OsLeaveMutex(){ - assert( inMutex ); - inMutex = 0; -#ifdef SQLITE_MACOS_MULTITASKING - MPExitCriticalRegion(criticalRegion); -#endif -} - -/* -** Turn a relative pathname into a full pathname. Return a pointer -** to the full pathname stored in space obtained from sqliteMalloc(). -** The calling function is responsible for freeing this space once it -** is no longer needed. -*/ -char *sqlite3OsFullPathname(const char *zRelative){ - char *zFull = 0; - if( zRelative[0]==':' ){ - char zBuf[_MAX_PATH+1]; - sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]), - (char*)0); - }else{ - if( strchr(zRelative, ':') ){ - sqlite3SetString(&zFull, zRelative, (char*)0); - }else{ - char zBuf[_MAX_PATH+1]; - sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, (char*)0); - } - } - return zFull; -} - -/* -** The following variable, if set to a non-zero value, becomes the result -** returned from sqlite3OsCurrentTime(). This is used for testing. -*/ -#ifdef SQLITE_TEST -int sqlite3_current_time = 0; -#endif - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -int sqlite3OsCurrentTime(double *prNow){ - *prNow = 0.0; /**** FIX ME *****/ -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; - } -#endif - return 0; -} - -#endif /* OS_MAC */ diff --git a/kopete/plugins/statistics/sqlite/os_mac.h b/kopete/plugins/statistics/sqlite/os_mac.h deleted file mode 100644 index 5b60f818..00000000 --- a/kopete/plugins/statistics/sqlite/os_mac.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file defines OS-specific features of classic Mac. -** OS X uses the os_unix.h file, not this one. -*/ -#ifndef _SQLITE_OS_MAC_H_ -#define _SQLITE_OS_MAC_H_ - - -#include <unistd.h> -#include <Files.h> -#define SQLITE_TEMPNAME_SIZE _MAX_PATH -#define SQLITE_MIN_SLEEP_MS 17 - -/* -** The OsFile structure is a operating-system independing representation -** of an open file handle. It is defined differently for each architecture. -** -** This is the definition for class Mac. -*/ -typedef struct OsFile OsFile; -struct OsFile { - SInt16 refNum; /* Data fork/file reference number */ - SInt16 refNumRF; /* Resource fork reference number (for locking) */ - int locked; /* 0: unlocked, <0: write lock, >0: read lock */ - int delOnClose; /* True if file is to be deleted on close */ - char *pathToDel; /* Name of file to delete on close */ -}; - - -#endif /* _SQLITE_OS_MAC_H_ */ diff --git a/kopete/plugins/statistics/sqlite/os_unix.c b/kopete/plugins/statistics/sqlite/os_unix.c deleted file mode 100644 index 94fca701..00000000 --- a/kopete/plugins/statistics/sqlite/os_unix.c +++ /dev/null @@ -1,1276 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains code that is specific to Unix systems. -*/ -#include "sqliteInt.h" -#include "os.h" -#if OS_UNIX /* This file is used on unix only */ - - -#include <time.h> -#include <errno.h> -#include <unistd.h> -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifdef SQLITE_DISABLE_LFS -# undef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifndef O_NOFOLLOW -# define O_NOFOLLOW 0 -#endif -#ifndef O_BINARY -# define O_BINARY 0 -#endif - - -/* -** The DJGPP compiler environment looks mostly like Unix, but it -** lacks the fcntl() system call. So redefine fcntl() to be something -** that always succeeds. This means that locking does not occur under -** DJGPP. But its DOS - what did you expect? -*/ -#ifdef __DJGPP__ -# define fcntl(A,B,C) 0 -#endif - -/* -** Macros used to determine whether or not to use threads. The -** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for -** Posix threads and SQLITE_W32_THREADS is defined if we are -** synchronizing using Win32 threads. -*/ -#if defined(THREADSAFE) && THREADSAFE -# include <pthread.h> -# define SQLITE_UNIX_THREADS 1 -#endif - - -/* -** Include code that is common to all os_*.c files -*/ -#include "os_common.h" - -#if defined(THREADSAFE) && THREADSAFE && defined(__linux__) -#define getpid pthread_self -#endif - -/* -** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996) -** section 6.5.2.2 lines 483 through 490 specify that when a process -** sets or clears a lock, that operation overrides any prior locks set -** by the same process. It does not explicitly say so, but this implies -** that it overrides locks set by the same process using a different -** file descriptor. Consider this test case: -** -** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); -** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); -** -** Suppose ./file1 and ./file2 are really the same file (because -** one is a hard or symbolic link to the other) then if you set -** an exclusive lock on fd1, then try to get an exclusive lock -** on fd2, it works. I would have expected the second lock to -** fail since there was already a lock on the file due to fd1. -** But not so. Since both locks came from the same process, the -** second overrides the first, even though they were on different -** file descriptors opened on different file names. -** -** Bummer. If you ask me, this is broken. Badly broken. It means -** that we cannot use POSIX locks to synchronize file access among -** competing threads of the same process. POSIX locks will work fine -** to synchronize access for threads in separate processes, but not -** threads within the same process. -** -** To work around the problem, SQLite has to manage file locks internally -** on its own. Whenever a new database is opened, we have to find the -** specific inode of the database file (the inode is determined by the -** st_dev and st_ino fields of the stat structure that fstat() fills in) -** and check for locks already existing on that inode. When locks are -** created or removed, we have to look at our own internal record of the -** locks to see if another thread has previously set a lock on that same -** inode. -** -** The OsFile structure for POSIX is no longer just an integer file -** descriptor. It is now a structure that holds the integer file -** descriptor and a pointer to a structure that describes the internal -** locks on the corresponding inode. There is one locking structure -** per inode, so if the same inode is opened twice, both OsFile structures -** point to the same locking structure. The locking structure keeps -** a reference count (so we will know when to delete it) and a "cnt" -** field that tells us its internal lock status. cnt==0 means the -** file is unlocked. cnt==-1 means the file has an exclusive lock. -** cnt>0 means there are cnt shared locks on the file. -** -** Any attempt to lock or unlock a file first checks the locking -** structure. The fcntl() system call is only invoked to set a -** POSIX lock if the internal lock structure transitions between -** a locked and an unlocked state. -** -** 2004-Jan-11: -** More recent discoveries about POSIX advisory locks. (The more -** I discover, the more I realize the a POSIX advisory locks are -** an abomination.) -** -** If you close a file descriptor that points to a file that has locks, -** all locks on that file that are owned by the current process are -** released. To work around this problem, each OsFile structure contains -** a pointer to an openCnt structure. There is one openCnt structure -** per open inode, which means that multiple OsFiles can point to a single -** openCnt. When an attempt is made to close an OsFile, if there are -** other OsFiles open on the same inode that are holding locks, the call -** to close() the file descriptor is deferred until all of the locks clear. -** The openCnt structure keeps a list of file descriptors that need to -** be closed and that list is walked (and cleared) when the last lock -** clears. -** -** First, under Linux threads, because each thread has a separate -** process ID, lock operations in one thread do not override locks -** to the same file in other threads. Linux threads behave like -** separate processes in this respect. But, if you close a file -** descriptor in linux threads, all locks are cleared, even locks -** on other threads and even though the other threads have different -** process IDs. Linux threads is inconsistent in this respect. -** (I'm beginning to think that linux threads is an abomination too.) -** The consequence of this all is that the hash table for the lockInfo -** structure has to include the process id as part of its key because -** locks in different threads are treated as distinct. But the -** openCnt structure should not include the process id in its -** key because close() clears lock on all threads, not just the current -** thread. Were it not for this goofiness in linux threads, we could -** combine the lockInfo and openCnt structures into a single structure. -** -** 2004-Jun-28: -** On some versions of linux, threads can override each others locks. -** On others not. Sometimes you can change the behavior on the same -** system by setting the LD_ASSUME_KERNEL environment variable. The -** POSIX standard is silent as to which behavior is correct, as far -** as I can tell, so other versions of unix might show the same -** inconsistency. There is no little doubt in my mind that posix -** advisory locks and linux threads are profoundly broken. -** -** To work around the inconsistencies, we have to test at runtime -** whether or not threads can override each others locks. This test -** is run once, the first time any lock is attempted. A static -** variable is set to record the results of this test for future -** use. -*/ - -/* -** An instance of the following structure serves as the key used -** to locate a particular lockInfo structure given its inode. -** -** If threads cannot override each others locks, then we set the -** lockKey.tid field to the thread ID. If threads can override -** each others locks then tid is always set to zero. tid is also -** set to zero if we compile without threading support. -*/ -struct lockKey { - dev_t dev; /* Device number */ - ino_t ino; /* Inode number */ -#ifdef SQLITE_UNIX_THREADS - pthread_t tid; /* Thread ID or zero if threads cannot override each other */ -#endif -}; - -/* -** An instance of the following structure is allocated for each open -** inode on each thread with a different process ID. (Threads have -** different process IDs on linux, but not on most other unixes.) -** -** A single inode can have multiple file descriptors, so each OsFile -** structure contains a pointer to an instance of this object and this -** object keeps a count of the number of OsFiles pointing to it. -*/ -struct lockInfo { - struct lockKey key; /* The lookup key */ - int cnt; /* Number of SHARED locks held */ - int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ - int nRef; /* Number of pointers to this structure */ -}; - -/* -** An instance of the following structure serves as the key used -** to locate a particular openCnt structure given its inode. This -** is the same as the lockKey except that the thread ID is omitted. -*/ -struct openKey { - dev_t dev; /* Device number */ - ino_t ino; /* Inode number */ -}; - -/* -** An instance of the following structure is allocated for each open -** inode. This structure keeps track of the number of locks on that -** inode. If a close is attempted against an inode that is holding -** locks, the close is deferred until all locks clear by adding the -** file descriptor to be closed to the pending list. -*/ -struct openCnt { - struct openKey key; /* The lookup key */ - int nRef; /* Number of pointers to this structure */ - int nLock; /* Number of outstanding locks */ - int nPending; /* Number of pending close() operations */ - int *aPending; /* Malloced space holding fd's awaiting a close() */ -}; - -/* -** These hash table maps inodes and process IDs into lockInfo and openCnt -** structures. Access to these hash tables must be protected by a mutex. -*/ -static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; -static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; - - -#ifdef SQLITE_UNIX_THREADS -/* -** This variable records whether or not threads can override each others -** locks. -** -** 0: No. Threads cannot override each others locks. -** 1: Yes. Threads can override each others locks. -** -1: We don't know yet. -*/ -static int threadsOverrideEachOthersLocks = -1; - -/* -** This structure holds information passed into individual test -** threads by the testThreadLockingBehavior() routine. -*/ -struct threadTestData { - int fd; /* File to be locked */ - struct flock lock; /* The locking operation */ - int result; /* Result of the locking operation */ -}; - -/* -** The testThreadLockingBehavior() routine launches two separate -** threads on this routine. This routine attempts to lock a file -** descriptor then returns. The success or failure of that attempt -** allows the testThreadLockingBehavior() procedure to determine -** whether or not threads can override each others locks. -*/ -static void *threadLockingTest(void *pArg){ - struct threadTestData *pData = (struct threadTestData*)pArg; - pData->result = fcntl(pData->fd, F_SETLK, &pData->lock); - return pArg; -} - -/* -** This procedure attempts to determine whether or not threads -** can override each others locks then sets the -** threadsOverrideEachOthersLocks variable appropriately. -*/ -static void testThreadLockingBehavior(fd_orig){ - int fd; - struct threadTestData d[2]; - pthread_t t[2]; - - fd = dup(fd_orig); - if( fd<0 ) return; - memset(d, 0, sizeof(d)); - d[0].fd = fd; - d[0].lock.l_type = F_RDLCK; - d[0].lock.l_len = 1; - d[0].lock.l_start = 0; - d[0].lock.l_whence = SEEK_SET; - d[1] = d[0]; - d[1].lock.l_type = F_WRLCK; - pthread_create(&t[0], 0, threadLockingTest, &d[0]); - pthread_create(&t[1], 0, threadLockingTest, &d[1]); - pthread_join(t[0], 0); - pthread_join(t[1], 0); - close(fd); - threadsOverrideEachOthersLocks = d[0].result==0 && d[1].result==0; -} -#endif /* SQLITE_UNIX_THREADS */ - -/* -** Release a lockInfo structure previously allocated by findLockInfo(). -*/ -static void releaseLockInfo(struct lockInfo *pLock){ - pLock->nRef--; - if( pLock->nRef==0 ){ - sqlite3HashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0); - sqliteFree(pLock); - } -} - -/* -** Release a openCnt structure previously allocated by findLockInfo(). -*/ -static void releaseOpenCnt(struct openCnt *pOpen){ - pOpen->nRef--; - if( pOpen->nRef==0 ){ - sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); - sqliteFree(pOpen->aPending); - sqliteFree(pOpen); - } -} - -/* -** Given a file descriptor, locate lockInfo and openCnt structures that -** describes that file descriptor. Create a new ones if necessary. The -** return values might be unset if an error occurs. -** -** Return the number of errors. -*/ -static int findLockInfo( - int fd, /* The file descriptor used in the key */ - struct lockInfo **ppLock, /* Return the lockInfo structure here */ - struct openCnt **ppOpen /* Return the openCnt structure here */ -){ - int rc; - struct lockKey key1; - struct openKey key2; - struct stat statbuf; - struct lockInfo *pLock; - struct openCnt *pOpen; - rc = fstat(fd, &statbuf); - if( rc!=0 ) return 1; - memset(&key1, 0, sizeof(key1)); - key1.dev = statbuf.st_dev; - key1.ino = statbuf.st_ino; -#ifdef SQLITE_UNIX_THREADS - if( threadsOverrideEachOthersLocks<0 ){ - testThreadLockingBehavior(fd); - } - key1.tid = threadsOverrideEachOthersLocks ? 0 : pthread_self(); -#endif - memset(&key2, 0, sizeof(key2)); - key2.dev = statbuf.st_dev; - key2.ino = statbuf.st_ino; - pLock = (struct lockInfo*)sqlite3HashFind(&lockHash, &key1, sizeof(key1)); - if( pLock==0 ){ - struct lockInfo *pOld; - pLock = sqliteMallocRaw( sizeof(*pLock) ); - if( pLock==0 ) return 1; - pLock->key = key1; - pLock->nRef = 1; - pLock->cnt = 0; - pLock->locktype = 0; - pOld = sqlite3HashInsert(&lockHash, &pLock->key, sizeof(key1), pLock); - if( pOld!=0 ){ - assert( pOld==pLock ); - sqliteFree(pLock); - return 1; - } - }else{ - pLock->nRef++; - } - *ppLock = pLock; - pOpen = (struct openCnt*)sqlite3HashFind(&openHash, &key2, sizeof(key2)); - if( pOpen==0 ){ - struct openCnt *pOld; - pOpen = sqliteMallocRaw( sizeof(*pOpen) ); - if( pOpen==0 ){ - releaseLockInfo(pLock); - return 1; - } - pOpen->key = key2; - pOpen->nRef = 1; - pOpen->nLock = 0; - pOpen->nPending = 0; - pOpen->aPending = 0; - pOld = sqlite3HashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen); - if( pOld!=0 ){ - assert( pOld==pOpen ); - sqliteFree(pOpen); - releaseLockInfo(pLock); - return 1; - } - }else{ - pOpen->nRef++; - } - *ppOpen = pOpen; - return 0; -} - -/* -** Delete the named file -*/ -int sqlite3OsDelete(const char *zFilename){ - unlink(zFilename); - return SQLITE_OK; -} - -/* -** Return TRUE if the named file exists. -*/ -int sqlite3OsFileExists(const char *zFilename){ - return access(zFilename, 0)==0; -} - -/* -** Attempt to open a file for both reading and writing. If that -** fails, try opening it read-only. If the file does not exist, -** try to create it. -** -** On success, a handle for the open file is written to *id -** and *pReadonly is set to 0 if the file was opened for reading and -** writing or 1 if the file was opened read-only. The function returns -** SQLITE_OK. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id and *pReadonly unchanged. -*/ -int sqlite3OsOpenReadWrite( - const char *zFilename, - OsFile *id, - int *pReadonly -){ - int rc; - assert( !id->isOpen ); - id->dirfd = -1; - id->h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644); - if( id->h<0 ){ -#ifdef EISDIR - if( errno==EISDIR ){ - return SQLITE_CANTOPEN; - } -#endif - id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); - if( id->h<0 ){ - return SQLITE_CANTOPEN; - } - *pReadonly = 1; - }else{ - *pReadonly = 0; - } - sqlite3OsEnterMutex(); - rc = findLockInfo(id->h, &id->pLock, &id->pOpen); - sqlite3OsLeaveMutex(); - if( rc ){ - close(id->h); - return SQLITE_NOMEM; - } - id->locktype = 0; - id->isOpen = 1; - TRACE3("OPEN %-3d %s\n", id->h, zFilename); - OpenCounter(+1); - return SQLITE_OK; -} - - -/* -** Attempt to open a new file for exclusive access by this process. -** The file will be opened for both reading and writing. To avoid -** a potential security problem, we do not allow the file to have -** previously existed. Nor do we allow the file to be a symbolic -** link. -** -** If delFlag is true, then make arrangements to automatically delete -** the file when it is closed. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ - int rc; - assert( !id->isOpen ); - if( access(zFilename, 0)==0 ){ - return SQLITE_CANTOPEN; - } - id->dirfd = -1; - id->h = open(zFilename, - O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600); - if( id->h<0 ){ - return SQLITE_CANTOPEN; - } - sqlite3OsEnterMutex(); - rc = findLockInfo(id->h, &id->pLock, &id->pOpen); - sqlite3OsLeaveMutex(); - if( rc ){ - close(id->h); - unlink(zFilename); - return SQLITE_NOMEM; - } - id->locktype = 0; - id->isOpen = 1; - if( delFlag ){ - unlink(zFilename); - } - TRACE3("OPEN-EX %-3d %s\n", id->h, zFilename); - OpenCounter(+1); - return SQLITE_OK; -} - -/* -** Attempt to open a new file for read-only access. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ - int rc; - assert( !id->isOpen ); - id->dirfd = -1; - id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); - if( id->h<0 ){ - return SQLITE_CANTOPEN; - } - sqlite3OsEnterMutex(); - rc = findLockInfo(id->h, &id->pLock, &id->pOpen); - sqlite3OsLeaveMutex(); - if( rc ){ - close(id->h); - return SQLITE_NOMEM; - } - id->locktype = 0; - id->isOpen = 1; - TRACE3("OPEN-RO %-3d %s\n", id->h, zFilename); - OpenCounter(+1); - return SQLITE_OK; -} - -/* -** Attempt to open a file descriptor for the directory that contains a -** file. This file descriptor can be used to fsync() the directory -** in order to make sure the creation of a new file is actually written -** to disk. -** -** This routine is only meaningful for Unix. It is a no-op under -** windows since windows does not support hard links. -** -** On success, a handle for a previously open file is at *id is -** updated with the new directory file descriptor and SQLITE_OK is -** returned. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id unchanged. -*/ -int sqlite3OsOpenDirectory( - const char *zDirname, - OsFile *id -){ - if( !id->isOpen ){ - /* Do not open the directory if the corresponding file is not already - ** open. */ - return SQLITE_CANTOPEN; - } - assert( id->dirfd<0 ); - id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644); - if( id->dirfd<0 ){ - return SQLITE_CANTOPEN; - } - TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname); - return SQLITE_OK; -} - -/* -** If the following global variable points to a string which is the -** name of a directory, then that directory will be used to store -** temporary files. -*/ -const char *sqlite3_temp_directory = 0; - -/* -** Create a temporary file name in zBuf. zBuf must be big enough to -** hold at least SQLITE_TEMPNAME_SIZE characters. -*/ -int sqlite3OsTempFileName(char *zBuf){ - static const char *azDirs[] = { - 0, - "/var/tmp", - "/usr/tmp", - "/tmp", - ".", - }; - static const unsigned char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - int i, j; - struct stat buf; - const char *zDir = "."; - azDirs[0] = sqlite3_temp_directory; - for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){ - if( azDirs[i]==0 ) continue; - if( stat(azDirs[i], &buf) ) continue; - if( !S_ISDIR(buf.st_mode) ) continue; - if( access(azDirs[i], 07) ) continue; - zDir = azDirs[i]; - break; - } - do{ - sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir); - j = strlen(zBuf); - sqlite3Randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - }while( access(zBuf,0)==0 ); - return SQLITE_OK; -} - -/* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. -*/ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ - int got; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - TIMER_START; - got = read(id->h, pBuf, amt); - TIMER_END; - TRACE4("READ %-3d %7d %d\n", id->h, last_page, TIMER_ELAPSED); - SEEK(0); - /* if( got<0 ) got = 0; */ - if( got==amt ){ - return SQLITE_OK; - }else{ - return SQLITE_IOERR; - } -} - -/* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. -*/ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ - int wrote = 0; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - SimulateDiskfullError; - TIMER_START; - while( amt>0 && (wrote = write(id->h, pBuf, amt))>0 ){ - amt -= wrote; - pBuf = &((char*)pBuf)[wrote]; - } - TIMER_END; - TRACE4("WRITE %-3d %7d %d\n", id->h, last_page, TIMER_ELAPSED); - SEEK(0); - if( amt>0 ){ - return SQLITE_FULL; - } - return SQLITE_OK; -} - -/* -** Move the read/write pointer in a file. -*/ -int sqlite3OsSeek(OsFile *id, i64 offset){ - assert( id->isOpen ); - SEEK(offset/1024 + 1); - lseek(id->h, offset, SEEK_SET); - return SQLITE_OK; -} - -/* -** The fsync() system call does not work as advertised on many -** unix systems. The following procedure is an attempt to make -** it work better. -*/ -static int full_fsync(int fd){ - int rc; -#ifdef F_FULLFSYNC - rc = fcntl(fd, F_FULLFSYNC, 0); - if( rc ) rc = fsync(fd); -#else - rc = fsync(fd); -#endif - return rc; -} - -/* -** Make sure all writes to a particular file are committed to disk. -** -** Under Unix, also make sure that the directory entry for the file -** has been created by fsync-ing the directory that contains the file. -** If we do not do this and we encounter a power failure, the directory -** entry for the journal might not exist after we reboot. The next -** SQLite to access the file will not know that the journal exists (because -** the directory entry for the journal was never created) and the transaction -** will not roll back - possibly leading to database corruption. -*/ -int sqlite3OsSync(OsFile *id){ - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - TRACE2("SYNC %-3d\n", id->h); - if( full_fsync(id->h) ){ - return SQLITE_IOERR; - } - if( id->dirfd>=0 ){ - TRACE2("DIRSYNC %-3d\n", id->dirfd); - full_fsync(id->dirfd); - close(id->dirfd); /* Only need to sync once, so close the directory */ - id->dirfd = -1; /* when we are done. */ - } - return SQLITE_OK; -} - -/* -** Sync the directory zDirname. This is a no-op on operating systems other -** than UNIX. -*/ -int sqlite3OsSyncDirectory(const char *zDirname){ - int fd; - int r; - SimulateIOError(SQLITE_IOERR); - fd = open(zDirname, O_RDONLY|O_BINARY, 0644); - TRACE3("DIRSYNC %-3d (%s)\n", fd, zDirname); - if( fd<0 ){ - return SQLITE_CANTOPEN; - } - r = fsync(fd); - close(fd); - return ((r==0)?SQLITE_OK:SQLITE_IOERR); -} - -/* -** Truncate an open file to a specified size -*/ -int sqlite3OsTruncate(OsFile *id, i64 nByte){ - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - return ftruncate(id->h, nByte)==0 ? SQLITE_OK : SQLITE_IOERR; -} - -/* -** Determine the current size of a file in bytes -*/ -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ - struct stat buf; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - if( fstat(id->h, &buf)!=0 ){ - return SQLITE_IOERR; - } - *pSize = buf.st_size; - return SQLITE_OK; -} - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, return -** non-zero. If the file is unlocked or holds only SHARED locks, then -** return zero. -*/ -int sqlite3OsCheckReservedLock(OsFile *id){ - int r = 0; - - assert( id->isOpen ); - sqlite3OsEnterMutex(); /* Needed because id->pLock is shared across threads */ - - /* Check if a thread in this process holds such a lock */ - if( id->pLock->locktype>SHARED_LOCK ){ - r = 1; - } - - /* Otherwise see if some other process holds it. - */ - if( !r ){ - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = RESERVED_BYTE; - lock.l_len = 1; - lock.l_type = F_WRLCK; - fcntl(id->h, F_GETLK, &lock); - if( lock.l_type!=F_UNLCK ){ - r = 1; - } - } - - sqlite3OsLeaveMutex(); - TRACE3("TEST WR-LOCK %d %d\n", id->h, r); - - return r; -} - -#ifdef SQLITE_DEBUG -/* -** Helper function for printing out trace information from debugging -** binaries. This returns the string represetation of the supplied -** integer lock-type. -*/ -static const char * locktypeName(int locktype){ - switch( locktype ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; - } - return "ERROR"; -} -#endif - -/* -** Lock the file with the lock specified by parameter locktype - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -int sqlite3OsLock(OsFile *id, int locktype){ - /* The following describes the implementation of the various locks and - ** lock transitions in terms of the POSIX advisory shared and exclusive - ** lock primitives (called read-locks and write-locks below, to avoid - ** confusion with SQLite lock names). The algorithms are complicated - ** slightly in order to be compatible with windows systems simultaneously - ** accessing the same database file, in case that is ever required. - ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved - ** byte', each single bytes at well known offsets, and the 'shared byte - ** range', a range of 510 bytes at a well known offset. - ** - ** To obtain a SHARED lock, a read-lock is obtained on the 'pending - ** byte'. If this is successful, a random byte from the 'shared byte - ** range' is read-locked and the lock on the 'pending byte' released. - ** - ** A process may only obtain a RESERVED lock after it has a SHARED lock. - ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. - ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. - ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. - ** - ** The reason a single byte cannot be used instead of the 'shared byte - ** range' is that some versions of windows do not support read-locks. By - ** locking a random byte from a range, concurrent SHARED locks may exist - ** even if the locking primitive used is always a write-lock. - */ - int rc = SQLITE_OK; - struct lockInfo *pLock = id->pLock; - struct flock lock; - int s; - - assert( id->isOpen ); - TRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", id->h, locktypeName(locktype), - locktypeName(id->locktype), locktypeName(pLock->locktype), pLock->cnt - ,getpid() ); - - /* If there is already a lock of this type or more restrictive on the - ** OsFile, do nothing. Don't use the end_lock: exit path, as - ** sqlite3OsEnterMutex() hasn't been called yet. - */ - if( id->locktype>=locktype ){ - TRACE3("LOCK %d %s ok (already held)\n", id->h, locktypeName(locktype)); - return SQLITE_OK; - } - - /* Make sure the locking sequence is correct - */ - assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK ); - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK ); - - /* This mutex is needed because id->pLock is shared across threads - */ - sqlite3OsEnterMutex(); - - /* If some thread using this PID has a lock via a different OsFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (id->locktype!=pLock->locktype && - (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK)) - ){ - rc = SQLITE_BUSY; - goto end_lock; - } - - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return SQLITE_OK. - */ - if( locktype==SHARED_LOCK && - (pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){ - assert( locktype==SHARED_LOCK ); - assert( id->locktype==0 ); - assert( pLock->cnt>0 ); - id->locktype = SHARED_LOCK; - pLock->cnt++; - id->pOpen->nLock++; - goto end_lock; - } - - lock.l_len = 1L; - lock.l_whence = SEEK_SET; - - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - if( locktype==SHARED_LOCK - || (locktype==EXCLUSIVE_LOCK && id->locktype<PENDING_LOCK) - ){ - lock.l_type = (locktype==SHARED_LOCK?F_RDLCK:F_WRLCK); - lock.l_start = PENDING_BYTE; - s = fcntl(id->h, F_SETLK, &lock); - if( s ){ - rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; - goto end_lock; - } - } - - - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ - if( locktype==SHARED_LOCK ){ - assert( pLock->cnt==0 ); - assert( pLock->locktype==0 ); - - /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - s = fcntl(id->h, F_SETLK, &lock); - - /* Drop the temporary PENDING lock */ - lock.l_start = PENDING_BYTE; - lock.l_len = 1L; - lock.l_type = F_UNLCK; - fcntl(id->h, F_SETLK, &lock); - if( s ){ - rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; - }else{ - id->locktype = SHARED_LOCK; - id->pOpen->nLock++; - pLock->cnt = 1; - } - }else if( locktype==EXCLUSIVE_LOCK && pLock->cnt>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - assert( 0!=id->locktype ); - lock.l_type = F_WRLCK; - switch( locktype ){ - case RESERVED_LOCK: - lock.l_start = RESERVED_BYTE; - break; - case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - break; - default: - assert(0); - } - s = fcntl(id->h, F_SETLK, &lock); - if( s ){ - rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; - } - } - - if( rc==SQLITE_OK ){ - id->locktype = locktype; - pLock->locktype = locktype; - }else if( locktype==EXCLUSIVE_LOCK ){ - id->locktype = PENDING_LOCK; - pLock->locktype = PENDING_LOCK; - } - -end_lock: - sqlite3OsLeaveMutex(); - TRACE4("LOCK %d %s %s\n", id->h, locktypeName(locktype), - rc==SQLITE_OK ? "ok" : "failed"); - return rc; -} - -/* -** Lower the locking level on file descriptor id to locktype. locktype -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** It is not possible for this routine to fail if the second argument -** is NO_LOCK. If the second argument is SHARED_LOCK, this routine -** might return SQLITE_IOERR instead of SQLITE_OK. -*/ -int sqlite3OsUnlock(OsFile *id, int locktype){ - struct lockInfo *pLock; - struct flock lock; - int rc = SQLITE_OK; - - assert( id->isOpen ); - TRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", id->h, locktype, id->locktype, - id->pLock->locktype, id->pLock->cnt, getpid()); - - assert( locktype<=SHARED_LOCK ); - if( id->locktype<=locktype ){ - return SQLITE_OK; - } - sqlite3OsEnterMutex(); - pLock = id->pLock; - assert( pLock->cnt!=0 ); - if( id->locktype>SHARED_LOCK ){ - assert( pLock->locktype==id->locktype ); - if( locktype==SHARED_LOCK ){ - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( fcntl(id->h, F_SETLK, &lock)!=0 ){ - /* This should never happen */ - rc = SQLITE_IOERR; - } - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = PENDING_BYTE; - lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - fcntl(id->h, F_SETLK, &lock); - pLock->locktype = SHARED_LOCK; - } - if( locktype==NO_LOCK ){ - struct openCnt *pOpen; - - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - pLock->cnt--; - if( pLock->cnt==0 ){ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = lock.l_len = 0L; - fcntl(id->h, F_SETLK, &lock); - pLock->locktype = NO_LOCK; - } - - /* Decrement the count of locks against this same file. When the - ** count reaches zero, close any other file descriptors whose close - ** was deferred because of outstanding locks. - */ - pOpen = id->pOpen; - pOpen->nLock--; - assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; i<pOpen->nPending; i++){ - close(pOpen->aPending[i]); - } - sqliteFree(pOpen->aPending); - pOpen->nPending = 0; - pOpen->aPending = 0; - } - } - sqlite3OsLeaveMutex(); - id->locktype = locktype; - return rc; -} - -/* -** Close a file. -*/ -int sqlite3OsClose(OsFile *id){ - if( !id->isOpen ) return SQLITE_OK; - sqlite3OsUnlock(id, NO_LOCK); - if( id->dirfd>=0 ) close(id->dirfd); - id->dirfd = -1; - sqlite3OsEnterMutex(); - if( id->pOpen->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pOpen->aPending. It will be automatically closed when - ** the last lock is cleared. - */ - int *aNew; - struct openCnt *pOpen = id->pOpen; - pOpen->nPending++; - aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) ); - if( aNew==0 ){ - /* If a malloc fails, just leak the file descriptor */ - }else{ - pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending-1] = id->h; - } - }else{ - /* There are no outstanding locks so we can close the file immediately */ - close(id->h); - } - releaseLockInfo(id->pLock); - releaseOpenCnt(id->pOpen); - sqlite3OsLeaveMutex(); - id->isOpen = 0; - TRACE2("CLOSE %-3d\n", id->h); - OpenCounter(-1); - return SQLITE_OK; -} - -/* -** Get information to seed the random number generator. The seed -** is written into the buffer zBuf[256]. The calling function must -** supply a sufficiently large buffer. -*/ -int sqlite3OsRandomSeed(char *zBuf){ - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence.* This makes the - ** tests repeatable. - */ - memset(zBuf, 0, 256); -#if !defined(SQLITE_TEST) - { - int pid; - time((time_t*)zBuf); - pid = getpid(); - memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid)); - } -#endif - return SQLITE_OK; -} - -/* -** Sleep for a little while. Return the amount of time slept. -*/ -int sqlite3OsSleep(int ms){ -#if defined(HAVE_USLEEP) && HAVE_USLEEP - usleep(ms*1000); - return ms; -#else - sleep((ms+999)/1000); - return 1000*((ms+999)/1000); -#endif -} - -/* -** Static variables used for thread synchronization -*/ -static int inMutex = 0; -#ifdef SQLITE_UNIX_THREADS -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - -/* -** The following pair of routine implement mutual exclusion for -** multi-threaded processes. Only a single thread is allowed to -** executed code that is surrounded by EnterMutex() and LeaveMutex(). -** -** SQLite uses only a single Mutex. There is not much critical -** code and what little there is executes quickly and without blocking. -*/ -void sqlite3OsEnterMutex(){ -#ifdef SQLITE_UNIX_THREADS - pthread_mutex_lock(&mutex); -#endif - assert( !inMutex ); - inMutex = 1; -} -void sqlite3OsLeaveMutex(){ - assert( inMutex ); - inMutex = 0; -#ifdef SQLITE_UNIX_THREADS - pthread_mutex_unlock(&mutex); -#endif -} - -/* -** Turn a relative pathname into a full pathname. Return a pointer -** to the full pathname stored in space obtained from sqliteMalloc(). -** The calling function is responsible for freeing this space once it -** is no longer needed. -*/ -char *sqlite3OsFullPathname(const char *zRelative){ - char *zFull = 0; - if( zRelative[0]=='/' ){ - sqlite3SetString(&zFull, zRelative, (char*)0); - }else{ - char zBuf[5000]; - sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative, - (char*)0); - } - return zFull; -} - -/* -** The following variable, if set to a non-zero value, becomes the result -** returned from sqlite3OsCurrentTime(). This is used for testing. -*/ -#ifdef SQLITE_TEST -int sqlite3_current_time = 0; -#endif - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -int sqlite3OsCurrentTime(double *prNow){ - time_t t; - time(&t); - *prNow = t/86400.0 + 2440587.5; -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; - } -#endif - return 0; -} - -#if 0 /* NOT USED */ -/* -** Find the time that the file was last modified. Write the -** modification time and date as a Julian Day number into *prNow and -** return SQLITE_OK. Return SQLITE_ERROR if the modification -** time cannot be found. -*/ -int sqlite3OsFileModTime(OsFile *id, double *prNow){ - int rc; - struct stat statbuf; - if( fstat(id->h, &statbuf)==0 ){ - *prNow = statbuf.st_mtime/86400.0 + 2440587.5; - rc = SQLITE_OK; - }else{ - rc = SQLITE_ERROR; - } - return rc; -} -#endif /* NOT USED */ - -#endif /* OS_UNIX */ diff --git a/kopete/plugins/statistics/sqlite/os_unix.h b/kopete/plugins/statistics/sqlite/os_unix.h deleted file mode 100644 index 72f818be..00000000 --- a/kopete/plugins/statistics/sqlite/os_unix.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file defined OS-specific features for Unix. -*/ -#ifndef _SQLITE_OS_UNIX_H_ -#define _SQLITE_OS_UNIX_H_ - -/* -** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE -** to the compiler command line. -*/ - -/* -** These #defines should enable >2GB file support on Posix if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: RedHat 7.2) but you want your code to work -** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in RedHat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for MacOS. LFS is only supported on MacOS 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -/* -** standard include files. -*/ -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -/* -** The OsFile structure is a operating-system independing representation -** of an open file handle. It is defined differently for each architecture. -** -** This is the definition for Unix. -** -** OsFile.locktype takes one of the values SHARED_LOCK, RESERVED_LOCK, -** PENDING_LOCK or EXCLUSIVE_LOCK. -*/ -typedef struct OsFile OsFile; -struct OsFile { - struct Pager *pPager; /* The pager that owns this OsFile. Might be 0 */ - struct openCnt *pOpen; /* Info about all open fd's on this inode */ - struct lockInfo *pLock; /* Info about locks on this inode */ - int h; /* The file descriptor */ - unsigned char locktype; /* The type of lock held on this fd */ - unsigned char isOpen; /* True if needs to be closed */ - int dirfd; /* File descriptor for the directory */ -}; - -/* -** Maximum number of characters in a temporary file name -*/ -#define SQLITE_TEMPNAME_SIZE 200 - -/* -** Minimum interval supported by sqlite3OsSleep(). -*/ -#if defined(HAVE_USLEEP) && HAVE_USLEEP -# define SQLITE_MIN_SLEEP_MS 1 -#else -# define SQLITE_MIN_SLEEP_MS 1000 -#endif - - -#endif /* _SQLITE_OS_UNIX_H_ */ diff --git a/kopete/plugins/statistics/sqlite/os_win.c b/kopete/plugins/statistics/sqlite/os_win.c deleted file mode 100644 index f6e3e3ea..00000000 --- a/kopete/plugins/statistics/sqlite/os_win.c +++ /dev/null @@ -1,747 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains code that is specific to windows. -*/ -#include "sqliteInt.h" -#include "os.h" -#if OS_WIN /* This file is used for windows only */ - -#include <winbase.h> - -/* -** Macros used to determine whether or not to use threads. -*/ -#if defined(THREADSAFE) && THREADSAFE -# define SQLITE_W32_THREADS 1 -#endif - -/* -** Include code that is common to all os_*.c files -*/ -#include "os_common.h" - -/* -** Delete the named file -*/ -int sqlite3OsDelete(const char *zFilename){ - DeleteFileA(zFilename); - TRACE2("DELETE \"%s\"\n", zFilename); - return SQLITE_OK; -} - -/* -** Return TRUE if the named file exists. -*/ -int sqlite3OsFileExists(const char *zFilename){ - return GetFileAttributesA(zFilename) != 0xffffffff; -} - -/* -** Attempt to open a file for both reading and writing. If that -** fails, try opening it read-only. If the file does not exist, -** try to create it. -** -** On success, a handle for the open file is written to *id -** and *pReadonly is set to 0 if the file was opened for reading and -** writing or 1 if the file was opened read-only. The function returns -** SQLITE_OK. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id and *pReadonly unchanged. -*/ -int sqlite3OsOpenReadWrite( - const char *zFilename, - OsFile *id, - int *pReadonly -){ - HANDLE h; - assert( !id->isOpen ); - h = CreateFileA(zFilename, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - NULL - ); - if( h==INVALID_HANDLE_VALUE ){ - h = CreateFileA(zFilename, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - NULL - ); - if( h==INVALID_HANDLE_VALUE ){ - return SQLITE_CANTOPEN; - } - *pReadonly = 1; - }else{ - *pReadonly = 0; - } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); - TRACE3("OPEN R/W %d \"%s\"\n", h, zFilename); - return SQLITE_OK; -} - - -/* -** Attempt to open a new file for exclusive access by this process. -** The file will be opened for both reading and writing. To avoid -** a potential security problem, we do not allow the file to have -** previously existed. Nor do we allow the file to be a symbolic -** link. -** -** If delFlag is true, then make arrangements to automatically delete -** the file when it is closed. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ - HANDLE h; - int fileflags; - assert( !id->isOpen ); - if( delFlag ){ - fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS - | FILE_FLAG_DELETE_ON_CLOSE; - }else{ - fileflags = FILE_FLAG_RANDOM_ACCESS; - } - h = CreateFileA(zFilename, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - fileflags, - NULL - ); - if( h==INVALID_HANDLE_VALUE ){ - return SQLITE_CANTOPEN; - } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); - TRACE3("OPEN EX %d \"%s\"\n", h, zFilename); - return SQLITE_OK; -} - -/* -** Attempt to open a new file for read-only access. -** -** On success, write the file handle into *id and return SQLITE_OK. -** -** On failure, return SQLITE_CANTOPEN. -*/ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ - HANDLE h; - assert( !id->isOpen ); - h = CreateFileA(zFilename, - GENERIC_READ, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - NULL - ); - if( h==INVALID_HANDLE_VALUE ){ - return SQLITE_CANTOPEN; - } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); - TRACE3("OPEN RO %d \"%s\"\n", h, zFilename); - return SQLITE_OK; -} - -/* -** Attempt to open a file descriptor for the directory that contains a -** file. This file descriptor can be used to fsync() the directory -** in order to make sure the creation of a new file is actually written -** to disk. -** -** This routine is only meaningful for Unix. It is a no-op under -** windows since windows does not support hard links. -** -** On success, a handle for a previously open file is at *id is -** updated with the new directory file descriptor and SQLITE_OK is -** returned. -** -** On failure, the function returns SQLITE_CANTOPEN and leaves -** *id unchanged. -*/ -int sqlite3OsOpenDirectory( - const char *zDirname, - OsFile *id -){ - return SQLITE_OK; -} - -/* -** If the following global variable points to a string which is the -** name of a directory, then that directory will be used to store -** temporary files. -*/ -const char *sqlite3_temp_directory = 0; - -/* -** Create a temporary file name in zBuf. zBuf must be big enough to -** hold at least SQLITE_TEMPNAME_SIZE characters. -*/ -int sqlite3OsTempFileName(char *zBuf){ - static char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - int i, j; - char zTempPath[SQLITE_TEMPNAME_SIZE]; - if( sqlite3_temp_directory ){ - strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30); - zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; - }else{ - GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zTempPath); - } - for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} - zTempPath[i] = 0; - for(;;){ - sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath); - j = strlen(zBuf); - sqlite3Randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - if( !sqlite3OsFileExists(zBuf) ) break; - } - TRACE2("TEMP FILENAME: %s\n", zBuf); - return SQLITE_OK; -} - -/* -** Close a file. -*/ -int sqlite3OsClose(OsFile *id){ - if( id->isOpen ){ - TRACE2("CLOSE %d\n", id->h); - CloseHandle(id->h); - OpenCounter(-1); - id->isOpen = 0; - } - return SQLITE_OK; -} - -/* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. -*/ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ - DWORD got; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - TRACE3("READ %d lock=%d\n", id->h, id->locktype); - if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ - got = 0; - } - if( got==(DWORD)amt ){ - return SQLITE_OK; - }else{ - return SQLITE_IOERR; - } -} - -/* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. -*/ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ - int rc; - DWORD wrote; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - SimulateDiskfullError; - TRACE3("WRITE %d lock=%d\n", id->h, id->locktype); - while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ - amt -= wrote; - pBuf = &((char*)pBuf)[wrote]; - } - if( !rc || amt>(int)wrote ){ - return SQLITE_FULL; - } - return SQLITE_OK; -} - -/* -** Move the read/write pointer in a file. -*/ -int sqlite3OsSeek(OsFile *id, i64 offset){ - LONG upperBits = offset>>32; - LONG lowerBits = offset & 0xffffffff; - DWORD rc; - assert( id->isOpen ); - SEEK(offset/1024 + 1); - rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); - TRACE3("SEEK %d %lld\n", id->h, offset); - return SQLITE_OK; -} - -/* -** Make sure all writes to a particular file are committed to disk. -*/ -int sqlite3OsSync(OsFile *id){ - assert( id->isOpen ); - TRACE3("SYNC %d lock=%d\n", id->h, id->locktype); - if( FlushFileBuffers(id->h) ){ - return SQLITE_OK; - }else{ - return SQLITE_IOERR; - } -} - -/* -** Sync the directory zDirname. This is a no-op on operating systems other -** than UNIX. -*/ -int sqlite3OsSyncDirectory(const char *zDirname){ - SimulateIOError(SQLITE_IOERR); - return SQLITE_OK; -} - -/* -** Truncate an open file to a specified size -*/ -int sqlite3OsTruncate(OsFile *id, i64 nByte){ - LONG upperBits = nByte>>32; - assert( id->isOpen ); - TRACE3("TRUNCATE %d %lld\n", id->h, nByte); - SimulateIOError(SQLITE_IOERR); - SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); - SetEndOfFile(id->h); - return SQLITE_OK; -} - -/* -** Determine the current size of a file in bytes -*/ -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ - DWORD upperBits, lowerBits; - assert( id->isOpen ); - SimulateIOError(SQLITE_IOERR); - lowerBits = GetFileSize(id->h, &upperBits); - *pSize = (((i64)upperBits)<<32) + lowerBits; - return SQLITE_OK; -} - -/* -** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. -** Return false (zero) for Win95, Win98, or WinME. -** -** Here is an interesting observation: Win95, Win98, and WinME lack -** the LockFileEx() API. But we can still statically link against that -** API as long as we don't call it win running Win95/98/ME. A call to -** this routine is used to determine if the host is Win95/98/ME or -** WinNT/2K/XP so that we will know whether or not we can safely call -** the LockFileEx() API. -*/ -static int isNT(void){ - static int osType = 0; /* 0=unknown 1=win95 2=winNT */ - if( osType==0 ){ - OSVERSIONINFO sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - GetVersionEx(&sInfo); - osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; - } - return osType==2; -} - -/* -** Acquire a reader lock. -** Different API routines are called depending on whether or not this -** is Win95 or WinNT. -*/ -static int getReadLock(OsFile *id){ - int res; - if( isNT() ){ - OVERLAPPED ovlp; - ovlp.Offset = SHARED_FIRST; - ovlp.OffsetHigh = 0; - ovlp.hEvent = 0; - res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, 0, SHARED_SIZE,0,&ovlp); - }else{ - int lk; - sqlite3Randomness(sizeof(lk), &lk); - id->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); - res = LockFile(id->h, SHARED_FIRST+id->sharedLockByte, 0, 1, 0); - } - return res; -} - -/* -** Undo a readlock -*/ -static int unlockReadLock(OsFile *id){ - int res; - if( isNT() ){ - res = UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - }else{ - res = UnlockFile(id->h, SHARED_FIRST + id->sharedLockByte, 0, 1, 0); - } - return res; -} - -/* -** Lock the file with the lock specified by parameter locktype - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. The sqlite3OsUnlock() routine -** erases all locks at once and returns us immediately to locking level 0. -** It is not possible to lower the locking level one step at a time. You -** must go straight to locking level 0. -*/ -int sqlite3OsLock(OsFile *id, int locktype){ - int rc = SQLITE_OK; /* Return code from subroutines */ - int res = 1; /* Result of a windows lock call */ - int newLocktype; /* Set id->locktype to this value before exiting */ - int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ - - assert( id->isOpen ); - TRACE5("LOCK %d %d was %d(%d)\n", - id->h, locktype, id->locktype, id->sharedLockByte); - - /* If there is already a lock of this type or more restrictive on the - ** OsFile, do nothing. Don't use the end_lock: exit path, as - ** sqlite3OsEnterMutex() hasn't been called yet. - */ - if( id->locktype>=locktype ){ - return SQLITE_OK; - } - - /* Make sure the locking sequence is correct - */ - assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK ); - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK ); - - /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or - ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of - ** the PENDING_LOCK byte is temporary. - */ - newLocktype = id->locktype; - if( id->locktype==NO_LOCK - || (locktype==EXCLUSIVE_LOCK && id->locktype==RESERVED_LOCK) - ){ - int cnt = 3; - while( cnt-->0 && (res = LockFile(id->h, PENDING_BYTE, 0, 1, 0))==0 ){ - /* Try 3 times to get the pending lock. The pending lock might be - ** held by another reader process who will release it momentarily. - */ - TRACE2("could not get a PENDING lock. cnt=%d\n", cnt); - Sleep(1); - } - gotPendingLock = res; - } - - /* Acquire a shared lock - */ - if( locktype==SHARED_LOCK && res ){ - assert( id->locktype==NO_LOCK ); - res = getReadLock(id); - if( res ){ - newLocktype = SHARED_LOCK; - } - } - - /* Acquire a RESERVED lock - */ - if( locktype==RESERVED_LOCK && res ){ - assert( id->locktype==SHARED_LOCK ); - res = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); - if( res ){ - newLocktype = RESERVED_LOCK; - } - } - - /* Acquire a PENDING lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - newLocktype = PENDING_LOCK; - gotPendingLock = 0; - } - - /* Acquire an EXCLUSIVE lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - assert( id->locktype>=SHARED_LOCK ); - res = unlockReadLock(id); - TRACE2("unreadlock = %d\n", res); - res = LockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( res ){ - newLocktype = EXCLUSIVE_LOCK; - }else{ - TRACE2("error-code = %d\n", GetLastError()); - } - } - - /* If we are holding a PENDING lock that ought to be released, then - ** release it now. - */ - if( gotPendingLock && locktype==SHARED_LOCK ){ - UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); - } - - /* Update the state of the lock has held in the file descriptor then - ** return the appropriate result code. - */ - if( res ){ - rc = SQLITE_OK; - }else{ - TRACE4("LOCK FAILED %d trying for %d but got %d\n", id->h, - locktype, newLocktype); - rc = SQLITE_BUSY; - } - id->locktype = newLocktype; - return rc; -} - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, return -** non-zero, otherwise zero. -*/ -int sqlite3OsCheckReservedLock(OsFile *id){ - int rc; - assert( id->isOpen ); - if( id->locktype>=RESERVED_LOCK ){ - rc = 1; - TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc); - }else{ - rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); - if( rc ){ - UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); - } - rc = !rc; - TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc); - } - return rc; -} - -/* -** Lower the locking level on file descriptor id to locktype. locktype -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** It is not possible for this routine to fail if the second argument -** is NO_LOCK. If the second argument is SHARED_LOCK then this routine -** might return SQLITE_IOERR; -*/ -int sqlite3OsUnlock(OsFile *id, int locktype){ - int type; - int rc = SQLITE_OK; - assert( id->isOpen ); - assert( locktype<=SHARED_LOCK ); - TRACE5("UNLOCK %d to %d was %d(%d)\n", id->h, locktype, - id->locktype, id->sharedLockByte); - type = id->locktype; - if( type>=EXCLUSIVE_LOCK ){ - UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !getReadLock(id) ){ - /* This should never happen. We should always be able to - ** reacquire the read lock */ - rc = SQLITE_IOERR; - } - } - if( type>=RESERVED_LOCK ){ - UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); - } - if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - unlockReadLock(id); - } - if( type>=PENDING_LOCK ){ - UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); - } - id->locktype = locktype; - return rc; -} - -/* -** Get information to seed the random number generator. The seed -** is written into the buffer zBuf[256]. The calling function must -** supply a sufficiently large buffer. -*/ -int sqlite3OsRandomSeed(char *zBuf){ - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence.* This makes the - ** tests repeatable. - */ - memset(zBuf, 0, 256); - GetSystemTime((LPSYSTEMTIME)zBuf); - return SQLITE_OK; -} - -/* -** Sleep for a little while. Return the amount of time slept. -*/ -int sqlite3OsSleep(int ms){ - Sleep(ms); - return ms; -} - -/* -** Static variables used for thread synchronization -*/ -static int inMutex = 0; -#ifdef SQLITE_W32_THREADS - static CRITICAL_SECTION cs; -#endif - -/* -** The following pair of routine implement mutual exclusion for -** multi-threaded processes. Only a single thread is allowed to -** executed code that is surrounded by EnterMutex() and LeaveMutex(). -** -** SQLite uses only a single Mutex. There is not much critical -** code and what little there is executes quickly and without blocking. -*/ -void sqlite3OsEnterMutex(){ -#ifdef SQLITE_W32_THREADS - static int isInit = 0; - while( !isInit ){ - static long lock = 0; - if( InterlockedIncrement(&lock)==1 ){ - InitializeCriticalSection(&cs); - isInit = 1; - }else{ - Sleep(1); - } - } - EnterCriticalSection(&cs); -#endif - assert( !inMutex ); - inMutex = 1; -} -void sqlite3OsLeaveMutex(){ - assert( inMutex ); - inMutex = 0; -#ifdef SQLITE_W32_THREADS - LeaveCriticalSection(&cs); -#endif -} - -/* -** Turn a relative pathname into a full pathname. Return a pointer -** to the full pathname stored in space obtained from sqliteMalloc(). -** The calling function is responsible for freeing this space once it -** is no longer needed. -*/ -char *sqlite3OsFullPathname(const char *zRelative){ - char *zNotUsed; - char *zFull; - int nByte; - nByte = GetFullPathNameA(zRelative, 0, 0, &zNotUsed) + 1; - zFull = sqliteMalloc( nByte ); - if( zFull==0 ) return 0; - GetFullPathNameA(zRelative, nByte, zFull, &zNotUsed); - return zFull; -} - -/* -** The following variable, if set to a non-zero value, becomes the result -** returned from sqlite3OsCurrentTime(). This is used for testing. -*/ -#ifdef SQLITE_TEST -int sqlite3_current_time = 0; -#endif - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -int sqlite3OsCurrentTime(double *prNow){ - FILETIME ft; - /* FILETIME structure is a 64-bit value representing the number of - 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). - */ - double now; - GetSystemTimeAsFileTime( &ft ); - now = ((double)ft.dwHighDateTime) * 4294967296.0; - *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; - } -#endif - return 0; -} - -/* -** Find the time that the file was last modified. Write the -** modification time and date as a Julian Day number into *prNow and -** return SQLITE_OK. Return SQLITE_ERROR if the modification -** time cannot be found. -*/ -int sqlite3OsFileModTime(OsFile *id, double *prMTime){ - int rc; - FILETIME ft; - /* FILETIME structure is a 64-bit value representing the number of - ** 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). - */ - if( GetFileTime(id->h, 0, 0, &ft) ){ - double t; - t = ((double)ft.dwHighDateTime) * 4294967296.0; - *prMTime = (t + ft.dwLowDateTime)/864000000000.0 + 2305813.5; - rc = SQLITE_OK; - }else{ - rc = SQLITE_ERROR; - } - return rc; -} - -#endif /* OS_WIN */ diff --git a/kopete/plugins/statistics/sqlite/os_win.h b/kopete/plugins/statistics/sqlite/os_win.h deleted file mode 100644 index baf937b2..00000000 --- a/kopete/plugins/statistics/sqlite/os_win.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file defines OS-specific features for Win32 -*/ -#ifndef _SQLITE_OS_WIN_H_ -#define _SQLITE_OS_WIN_H_ - -#include <windows.h> -#include <winbase.h> - -/* -** The OsFile structure is a operating-system independing representation -** of an open file handle. It is defined differently for each architecture. -** -** This is the definition for Win32. -*/ -typedef struct OsFile OsFile; -struct OsFile { - HANDLE h; /* Handle for accessing the file */ - unsigned char locktype; /* Type of lock currently held on this file */ - unsigned char isOpen; /* True if needs to be closed */ - short sharedLockByte; /* Randomly chosen byte used as a shared lock */ -}; - - -#define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) -#define SQLITE_MIN_SLEEP_MS 1 - - -#endif /* _SQLITE_OS_WIN_H_ */ diff --git a/kopete/plugins/statistics/sqlite/pager.c b/kopete/plugins/statistics/sqlite/pager.c deleted file mode 100644 index a374562b..00000000 --- a/kopete/plugins/statistics/sqlite/pager.c +++ /dev/null @@ -1,3205 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the implementation of the page cache subsystem or "pager". -** -** The pager is used to access a database disk file. It implements -** atomic commit and rollback through the use of a journal file that -** is separate from the database file. The pager also implements file -** locking to prevent two processes from writing the same database -** file simultaneously, or one process from reading the database while -** another is writing. -** -** @(#) $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" -#include "pager.h" -#include <assert.h> -#include <string.h> - -/* -** Macros for troubleshooting. Normally turned off -*/ -#if 0 -#define TRACE1(X) sqlite3DebugPrintf(X) -#define TRACE2(X,Y) sqlite3DebugPrintf(X,Y) -#define TRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z) -#define TRACE4(X,Y,Z,W) sqlite3DebugPrintf(X,Y,Z,W) -#else -#define TRACE1(X) -#define TRACE2(X,Y) -#define TRACE3(X,Y,Z) -#define TRACE4(X,Y,Z,W) -#endif - - -/* -** The page cache as a whole is always in one of the following -** states: -** -** PAGER_UNLOCK The page cache is not currently reading or -** writing the database file. There is no -** data held in memory. This is the initial -** state. -** -** PAGER_SHARED The page cache is reading the database. -** Writing is not permitted. There can be -** multiple readers accessing the same database -** file at the same time. -** -** PAGER_RESERVED This process has reserved the database for writing -** but has not yet made any changes. Only one process -** at a time can reserve the database. The original -** database file has not been modified so other -** processes may still be reading the on-disk -** database file. -** -** PAGER_EXCLUSIVE The page cache is writing the database. -** Access is exclusive. No other processes or -** threads can be reading or writing while one -** process is writing. -** -** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE -** after all dirty pages have been written to the -** database file and the file has been synced to -** disk. All that remains to do is to remove the -** journal file and the transaction will be -** committed. -** -** The page cache comes up in PAGER_UNLOCK. The first time a -** sqlite3pager_get() occurs, the state transitions to PAGER_SHARED. -** After all pages have been released using sqlite_page_unref(), -** the state transitions back to PAGER_UNLOCK. The first time -** that sqlite3pager_write() is called, the state transitions to -** PAGER_RESERVED. (Note that sqlite_page_write() can only be -** called on an outstanding page which means that the pager must -** be in PAGER_SHARED before it transitions to PAGER_RESERVED.) -** The transition to PAGER_EXCLUSIVE occurs when before any changes -** are made to the database file. After an sqlite3pager_rollback() -** or sqlite_pager_commit(), the state goes back to PAGER_SHARED. -*/ -#define PAGER_UNLOCK 0 -#define PAGER_SHARED 1 /* same as SHARED_LOCK */ -#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */ -#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */ -#define PAGER_SYNCED 5 - -/* -** If the SQLITE_BUSY_RESERVED_LOCK macro is set to true at compile-time, -** then failed attempts to get a reserved lock will invoke the busy callback. -** This is off by default. To see why, consider the following scenario: -** -** Suppose thread A already has a shared lock and wants a reserved lock. -** Thread B already has a reserved lock and wants an exclusive lock. If -** both threads are using their busy callbacks, it might be a long time -** be for one of the threads give up and allows the other to proceed. -** But if the thread trying to get the reserved lock gives up quickly -** (if it never invokes its busy callback) then the contention will be -** resolved quickly. -*/ -#ifndef SQLITE_BUSY_RESERVED_LOCK -# define SQLITE_BUSY_RESERVED_LOCK 0 -#endif - -/* -** Each in-memory image of a page begins with the following header. -** This header is only visible to this pager module. The client -** code that calls pager sees only the data that follows the header. -** -** Client code should call sqlite3pager_write() on a page prior to making -** any modifications to that page. The first time sqlite3pager_write() -** is called, the original page contents are written into the rollback -** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once -** the journal page has made it onto the disk surface, PgHdr.needSync -** is cleared. The modified page cannot be written back into the original -** database file until the journal pages has been synced to disk and the -** PgHdr.needSync has been cleared. -** -** The PgHdr.dirty flag is set when sqlite3pager_write() is called and -** is cleared again when the page content is written back to the original -** database file. -*/ -typedef struct PgHdr PgHdr; -struct PgHdr { - Pager *pPager; /* The pager to which this page belongs */ - Pgno pgno; /* The page number for this page */ - PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */ - PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */ - PgHdr *pNextAll; /* A list of all pages */ - PgHdr *pNextStmt, *pPrevStmt; /* List of pages in the statement journal */ - u8 inJournal; /* TRUE if has been written to journal */ - u8 inStmt; /* TRUE if in the statement subjournal */ - u8 dirty; /* TRUE if we need to write back changes */ - u8 needSync; /* Sync journal before writing this page */ - u8 alwaysRollback; /* Disable dont_rollback() for this page */ - short int nRef; /* Number of users of this page */ - PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */ - /* pPager->pageSize bytes of page data follow this header */ - /* Pager.nExtra bytes of local data follow the page data */ -}; - -/* -** For an in-memory only database, some extra information is recorded about -** each page so that changes can be rolled back. (Journal files are not -** used for in-memory databases.) The following information is added to -** the end of every EXTRA block for in-memory databases. -** -** This information could have been added directly to the PgHdr structure. -** But then it would take up an extra 8 bytes of storage on every PgHdr -** even for disk-based databases. Splitting it out saves 8 bytes. This -** is only a savings of 0.8% but those percentages add up. -*/ -typedef struct PgHistory PgHistory; -struct PgHistory { - u8 *pOrig; /* Original page text. Restore to this on a full rollback */ - u8 *pStmt; /* Text as it was at the beginning of the current statement */ -}; - -/* -** A macro used for invoking the codec if there is one -*/ -#ifdef SQLITE_HAS_CODEC -# define CODEC(P,D,N,X) if( P->xCodec ){ P->xCodec(P->pCodecArg,D,N,X); } -#else -# define CODEC(P,D,N,X) -#endif - -/* -** Convert a pointer to a PgHdr into a pointer to its data -** and back again. -*/ -#define PGHDR_TO_DATA(P) ((void*)(&(P)[1])) -#define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1]) -#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->pageSize]) -#define PGHDR_TO_HIST(P,PGR) \ - ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra]) - -/* -** How big to make the hash table used for locating in-memory pages -** by page number. -*/ -#define N_PG_HASH 2048 - -/* -** Hash a page number -*/ -#define pager_hash(PN) ((PN)&(N_PG_HASH-1)) - -/* -** A open page cache is an instance of the following structure. -*/ -struct Pager { - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - char *zDirectory; /* Directory hold database and journal files */ - OsFile fd, jfd; /* File descriptors for database and journal */ - OsFile stfd; /* File descriptor for the statement subjournal*/ - int dbSize; /* Number of pages in the file */ - int origDbSize; /* dbSize before the current change */ - int stmtSize; /* Size of database (in pages) at stmt_begin() */ - i64 stmtJSize; /* Size of journal at stmt_begin() */ - int nRec; /* Number of pages written to the journal */ - u32 cksumInit; /* Quasi-random value added to every checksum */ - int stmtNRec; /* Number of records in stmt subjournal */ - int nExtra; /* Add this many bytes to each in-memory page */ - void (*xDestructor)(void*,int); /* Call this routine when freeing pages */ - void (*xReiniter)(void*,int); /* Call this routine when reloading pages */ - int pageSize; /* Number of bytes in a page */ - int nPage; /* Total number of in-memory pages */ - int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */ - int mxPage; /* Maximum number of pages to hold in cache */ - int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */ - void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ - void *pCodecArg; /* First argument to xCodec() */ - u8 journalOpen; /* True if journal file descriptors is valid */ - u8 journalStarted; /* True if header of journal is synced */ - u8 useJournal; /* Use a rollback journal on this file */ - u8 stmtOpen; /* True if the statement subjournal is open */ - u8 stmtInUse; /* True we are in a statement subtransaction */ - u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/ - u8 noSync; /* Do not sync the journal if true */ - u8 fullSync; /* Do extra syncs of the journal for robustness */ - u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */ - u8 errMask; /* One of several kinds of errors */ - u8 tempFile; /* zFilename is a temporary file */ - u8 readOnly; /* True for a read-only database */ - u8 needSync; /* True if an fsync() is needed on the journal */ - u8 dirtyCache; /* True if cached pages have changed */ - u8 alwaysRollback; /* Disable dont_rollback() for all pages */ - u8 memDb; /* True to inhibit all file I/O */ - u8 *aInJournal; /* One bit for each page in the database file */ - u8 *aInStmt; /* One bit for each page in the database */ - u8 setMaster; /* True if a m-j name has been written to jrnl */ - BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */ - PgHdr *pFirst, *pLast; /* List of free pages */ - PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */ - PgHdr *pAll; /* List of all pages */ - PgHdr *pStmt; /* List of pages in the statement subjournal */ - i64 journalOff; /* Current byte offset in the journal file */ - i64 journalHdr; /* Byte offset to previous journal header */ - i64 stmtHdrOff; /* First journal header written this statement */ - i64 stmtCksum; /* cksumInit when statement was started */ - int sectorSize; /* Assumed sector size during rollback */ - PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number to PgHdr */ -}; - -/* -** These are bits that can be set in Pager.errMask. -*/ -#define PAGER_ERR_FULL 0x01 /* a write() failed */ -#define PAGER_ERR_MEM 0x02 /* malloc() failed */ -#define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */ -#define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */ -#define PAGER_ERR_DISK 0x10 /* general disk I/O error - bad hard drive? */ - -/* -** Journal files begin with the following magic string. The data -** was obtained from /dev/random. It is used only as a sanity check. -** -** Since version 2.8.0, the journal format contains additional sanity -** checking information. If the power fails while the journal is begin -** written, semi-random garbage data might appear in the journal -** file after power is restored. If an attempt is then made -** to roll the journal back, the database could be corrupted. The additional -** sanity checking data is an attempt to discover the garbage in the -** journal and ignore it. -** -** The sanity checking information for the new journal format consists -** of a 32-bit checksum on each page of data. The checksum covers both -** the page number and the pPager->pageSize bytes of data for the page. -** This cksum is initialized to a 32-bit random value that appears in the -** journal file right after the header. The random initializer is important, -** because garbage data that appears at the end of a journal is likely -** data that was once in other files that have now been deleted. If the -** garbage data came from an obsolete journal file, the checksums might -** be correct. But by initializing the checksum to random value which -** is different for every journal, we minimize that risk. -*/ -static const unsigned char aJournalMagic[] = { - 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7, -}; - -/* -** The size of the header and of each page in the journal is determined -** by the following macros. -*/ -#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) - -/* -** The journal header size for this pager. In the future, this could be -** set to some value read from the disk controller. The important -** characteristic is that it is the same size as a disk sector. -*/ -#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) - -#define PAGER_SECTOR_SIZE 512 - -/* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is -** reserved for working around a windows/posix incompatibility). It is -** used in the journal to signify that the remainder of the journal file -** is devoted to storing a master journal name - there are no more pages to -** roll back. See comments for function writeMasterJournal() for details. -*/ -#define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize)) - -/* -** Enable reference count tracking (for debugging) here: -*/ -#ifdef SQLITE_TEST - int pager3_refinfo_enable = 0; - static void pager_refinfo(PgHdr *p){ - static int cnt = 0; - if( !pager3_refinfo_enable ) return; - sqlite3DebugPrintf( - "REFCNT: %4d addr=%p nRef=%d\n", - p->pgno, PGHDR_TO_DATA(p), p->nRef - ); - cnt++; /* Something to set a breakpoint on */ - } -# define REFINFO(X) pager_refinfo(X) -#else -# define REFINFO(X) -#endif - -/* -** Read a 32-bit integer from the given file descriptor. Store the integer -** that is read in *pRes. Return SQLITE_OK if everything worked, or an -** error code is something goes wrong. -** -** All values are stored on disk as big-endian. -*/ -static int read32bits(OsFile *fd, u32 *pRes){ - u32 res; - int rc; - rc = sqlite3OsRead(fd, &res, sizeof(res)); - if( rc==SQLITE_OK ){ - unsigned char ac[4]; - memcpy(ac, &res, 4); - res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3]; - } - *pRes = res; - return rc; -} - -/* -** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK -** on success or an error code is something goes wrong. -*/ -static int write32bits(OsFile *fd, u32 val){ - unsigned char ac[4]; - ac[0] = (val>>24) & 0xff; - ac[1] = (val>>16) & 0xff; - ac[2] = (val>>8) & 0xff; - ac[3] = val & 0xff; - return sqlite3OsWrite(fd, ac, 4); -} - -/* -** Write the 32-bit integer 'val' into the page identified by page header -** 'p' at offset 'offset'. -*/ -static void store32bits(u32 val, PgHdr *p, int offset){ - unsigned char *ac; - ac = &((unsigned char*)PGHDR_TO_DATA(p))[offset]; - ac[0] = (val>>24) & 0xff; - ac[1] = (val>>16) & 0xff; - ac[2] = (val>>8) & 0xff; - ac[3] = val & 0xff; -} - -/* -** Read a 32-bit integer at offset 'offset' from the page identified by -** page header 'p'. -*/ -static u32 retrieve32bits(PgHdr *p, int offset){ - unsigned char *ac; - ac = &((unsigned char*)PGHDR_TO_DATA(p))[offset]; - return (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3]; -} - - -/* -** Convert the bits in the pPager->errMask into an approprate -** return code. -*/ -static int pager_errcode(Pager *pPager){ - int rc = SQLITE_OK; - if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL; - if( pPager->errMask & PAGER_ERR_DISK ) rc = SQLITE_IOERR; - if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL; - if( pPager->errMask & PAGER_ERR_MEM ) rc = SQLITE_NOMEM; - if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT; - return rc; -} - -/* -** When this is called the journal file for pager pPager must be open. -** The master journal file name is read from the end of the file and -** written into memory obtained from sqliteMalloc(). *pzMaster is -** set to point at the memory and SQLITE_OK returned. The caller must -** sqliteFree() *pzMaster. -** -** If no master journal file name is present *pzMaster is set to 0 and -** SQLITE_OK returned. -*/ -static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ - int rc; - u32 len; - i64 szJ; - u32 cksum; - int i; - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - - *pzMaster = 0; - - rc = sqlite3OsFileSize(pJrnl, &szJ); - if( rc!=SQLITE_OK || szJ<16 ) return rc; - - rc = sqlite3OsSeek(pJrnl, szJ-16); - if( rc!=SQLITE_OK ) return rc; - - rc = read32bits(pJrnl, &len); - if( rc!=SQLITE_OK ) return rc; - - rc = read32bits(pJrnl, &cksum); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3OsRead(pJrnl, aMagic, 8); - if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc; - - rc = sqlite3OsSeek(pJrnl, szJ-16-len); - if( rc!=SQLITE_OK ) return rc; - - *pzMaster = (char *)sqliteMalloc(len+1); - if( !*pzMaster ){ - return SQLITE_NOMEM; - } - rc = sqlite3OsRead(pJrnl, *pzMaster, len); - if( rc!=SQLITE_OK ){ - sqliteFree(*pzMaster); - *pzMaster = 0; - return rc; - } - - /* See if the checksum matches the master journal name */ - for(i=0; i<len; i++){ - cksum -= (*pzMaster)[i]; - } - if( cksum ){ - /* If the checksum doesn't add up, then one or more of the disk sectors - ** containing the master journal filename is corrupted. This means - ** definitely roll back, so just return SQLITE_OK and report a (nul) - ** master-journal filename. - */ - sqliteFree(*pzMaster); - *pzMaster = 0; - } - (*pzMaster)[len] = '\0'; - - return SQLITE_OK; -} - -/* -** Seek the journal file descriptor to the next sector boundary where a -** journal header may be read or written. Pager.journalOff is updated with -** the new seek offset. -** -** i.e for a sector size of 512: -** -** Input Offset Output Offset -** --------------------------------------- -** 0 0 -** 512 512 -** 100 512 -** 2000 2048 -** -*/ -static int seekJournalHdr(Pager *pPager){ - i64 offset = 0; - i64 c = pPager->journalOff; - if( c ){ - offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager); - } - assert( offset%JOURNAL_HDR_SZ(pPager)==0 ); - assert( offset>=c ); - assert( (offset-c)<JOURNAL_HDR_SZ(pPager) ); - pPager->journalOff = offset; - return sqlite3OsSeek(&pPager->jfd, pPager->journalOff); -} - -/* -** The journal file must be open when this routine is called. A journal -** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the -** current location. -** -** The format for the journal header is as follows: -** - 8 bytes: Magic identifying journal format. -** - 4 bytes: Number of records in journal, or -1 no-sync mode is on. -** - 4 bytes: Random number used for page hash. -** - 4 bytes: Initial database page count. -** - 4 bytes: Sector size used by the process that wrote this journal. -** -** Followed by (JOURNAL_HDR_SZ - 24) bytes of unused space. -*/ -static int writeJournalHdr(Pager *pPager){ - - int rc = seekJournalHdr(pPager); - if( rc ) return rc; - - pPager->journalHdr = pPager->journalOff; - if( pPager->stmtHdrOff==0 ){ - pPager->stmtHdrOff = pPager->journalHdr; - } - pPager->journalOff += JOURNAL_HDR_SZ(pPager); - - /* FIX ME: - ** - ** Possibly for a pager not in no-sync mode, the journal magic should not - ** be written until nRec is filled in as part of next syncJournal(). - ** - ** Actually maybe the whole journal header should be delayed until that - ** point. Think about this. - */ - rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); - - if( rc==SQLITE_OK ){ - /* The nRec Field. 0xFFFFFFFF for no-sync journals. */ - rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0); - } - if( rc==SQLITE_OK ){ - /* The random check-hash initialiser */ - sqlite3Randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); - rc = write32bits(&pPager->jfd, pPager->cksumInit); - } - if( rc==SQLITE_OK ){ - /* The initial database size */ - rc = write32bits(&pPager->jfd, pPager->dbSize); - } - if( rc==SQLITE_OK ){ - /* The assumed sector size for this process */ - rc = write32bits(&pPager->jfd, pPager->sectorSize); - } - - /* The journal header has been written successfully. Seek the journal - ** file descriptor to the end of the journal header sector. - */ - if( rc==SQLITE_OK ){ - sqlite3OsSeek(&pPager->jfd, pPager->journalOff-1); - rc = sqlite3OsWrite(&pPager->jfd, "\000", 1); - } - return rc; -} - -/* -** The journal file must be open when this is called. A journal header file -** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal -** file. See comments above function writeJournalHdr() for a description of -** the journal header format. -** -** If the header is read successfully, *nRec is set to the number of -** page records following this header and *dbSize is set to the size of the -** database before the transaction began, in pages. Also, pPager->cksumInit -** is set to the value read from the journal header. SQLITE_OK is returned -** in this case. -** -** If the journal header file appears to be corrupted, SQLITE_DONE is -** returned and *nRec and *dbSize are not set. If JOURNAL_HDR_SZ bytes -** cannot be read from the journal file an error code is returned. -*/ -static int readJournalHdr( - Pager *pPager, - i64 journalSize, - u32 *pNRec, - u32 *pDbSize -){ - int rc; - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - - rc = seekJournalHdr(pPager); - if( rc ) return rc; - - if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ - return SQLITE_DONE; - } - - rc = sqlite3OsRead(&pPager->jfd, aMagic, sizeof(aMagic)); - if( rc ) return rc; - - if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ - return SQLITE_DONE; - } - - rc = read32bits(&pPager->jfd, pNRec); - if( rc ) return rc; - - rc = read32bits(&pPager->jfd, &pPager->cksumInit); - if( rc ) return rc; - - rc = read32bits(&pPager->jfd, pDbSize); - if( rc ) return rc; - - /* Update the assumed sector-size to match the value used by - ** the process that created this journal. If this journal was - ** created by a process other than this one, then this routine - ** is being called from within pager_playback(). The local value - ** of Pager.sectorSize is restored at the end of that routine. - */ - rc = read32bits(&pPager->jfd, (u32 *)&pPager->sectorSize); - if( rc ) return rc; - - pPager->journalOff += JOURNAL_HDR_SZ(pPager); - rc = sqlite3OsSeek(&pPager->jfd, pPager->journalOff); - return rc; -} - - -/* -** Write the supplied master journal name into the journal file for pager -** pPager at the current location. The master journal name must be the last -** thing written to a journal file. If the pager is in full-sync mode, the -** journal file descriptor is advanced to the next sector boundary before -** anything is written. The format is: -** -** + 4 bytes: PAGER_MJ_PGNO. -** + N bytes: length of master journal name. -** + 4 bytes: N -** + 4 bytes: Master journal name checksum. -** + 8 bytes: aJournalMagic[]. -** -** The master journal page checksum is the sum of the bytes in the master -** journal name. -*/ -static int writeMasterJournal(Pager *pPager, const char *zMaster){ - int rc; - int len; - int i; - u32 cksum = 0; - - if( !zMaster || pPager->setMaster) return SQLITE_OK; - pPager->setMaster = 1; - - len = strlen(zMaster); - for(i=0; i<len; i++){ - cksum += zMaster[i]; - } - - /* If in full-sync mode, advance to the next disk sector before writing - ** the master journal name. This is in case the previous page written to - ** the journal has already been synced. - */ - if( pPager->fullSync ){ - rc = seekJournalHdr(pPager); - if( rc!=SQLITE_OK ) return rc; - } - pPager->journalOff += (len+20); - - rc = write32bits(&pPager->jfd, PAGER_MJ_PGNO(pPager)); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3OsWrite(&pPager->jfd, zMaster, len); - if( rc!=SQLITE_OK ) return rc; - - rc = write32bits(&pPager->jfd, len); - if( rc!=SQLITE_OK ) return rc; - - rc = write32bits(&pPager->jfd, cksum); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); - pPager->needSync = 1; - return rc; -} - -/* -** Add or remove a page from the list of all pages that are in the -** statement journal. -** -** The Pager keeps a separate list of pages that are currently in -** the statement journal. This helps the sqlite3pager_stmt_commit() -** routine run MUCH faster for the common case where there are many -** pages in memory but only a few are in the statement journal. -*/ -static void page_add_to_stmt_list(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - if( pPg->inStmt ) return; - assert( pPg->pPrevStmt==0 && pPg->pNextStmt==0 ); - pPg->pPrevStmt = 0; - if( pPager->pStmt ){ - pPager->pStmt->pPrevStmt = pPg; - } - pPg->pNextStmt = pPager->pStmt; - pPager->pStmt = pPg; - pPg->inStmt = 1; -} -static void page_remove_from_stmt_list(PgHdr *pPg){ - if( !pPg->inStmt ) return; - if( pPg->pPrevStmt ){ - assert( pPg->pPrevStmt->pNextStmt==pPg ); - pPg->pPrevStmt->pNextStmt = pPg->pNextStmt; - }else{ - assert( pPg->pPager->pStmt==pPg ); - pPg->pPager->pStmt = pPg->pNextStmt; - } - if( pPg->pNextStmt ){ - assert( pPg->pNextStmt->pPrevStmt==pPg ); - pPg->pNextStmt->pPrevStmt = pPg->pPrevStmt; - } - pPg->pNextStmt = 0; - pPg->pPrevStmt = 0; - pPg->inStmt = 0; -} - -/* -** Find a page in the hash table given its page number. Return -** a pointer to the page or NULL if not found. -*/ -static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *p = pPager->aHash[pager_hash(pgno)]; - while( p && p->pgno!=pgno ){ - p = p->pNextHash; - } - return p; -} - -/* -** Unlock the database and clear the in-memory cache. This routine -** sets the state of the pager back to what it was when it was first -** opened. Any outstanding pages are invalidated and subsequent attempts -** to access those pages will likely result in a coredump. -*/ -static void pager_reset(Pager *pPager){ - PgHdr *pPg, *pNext; - for(pPg=pPager->pAll; pPg; pPg=pNext){ - pNext = pPg->pNextAll; - sqliteFree(pPg); - } - pPager->pFirst = 0; - pPager->pFirstSynced = 0; - pPager->pLast = 0; - pPager->pAll = 0; - memset(pPager->aHash, 0, sizeof(pPager->aHash)); - pPager->nPage = 0; - if( pPager->state>=PAGER_RESERVED ){ - sqlite3pager_rollback(pPager); - } - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - pPager->state = PAGER_UNLOCK; - pPager->dbSize = -1; - pPager->nRef = 0; - assert( pPager->journalOpen==0 ); -} - -/* -** When this routine is called, the pager has the journal file open and -** a RESERVED or EXCLUSIVE lock on the database. This routine releases -** the database lock and acquires a SHARED lock in its place. The journal -** file is deleted and closed. -** -** TODO: Consider keeping the journal file open for temporary databases. -** This might give a performance improvement on windows where opening -** a file is an expensive operation. -*/ -static int pager_unwritelock(Pager *pPager){ - PgHdr *pPg; - int rc; - assert( !pPager->memDb ); - if( pPager->state<PAGER_RESERVED ){ - return SQLITE_OK; - } - sqlite3pager_stmt_commit(pPager); - if( pPager->stmtOpen ){ - sqlite3OsClose(&pPager->stfd); - pPager->stmtOpen = 0; - } - if( pPager->journalOpen ){ - sqlite3OsClose(&pPager->jfd); - pPager->journalOpen = 0; - sqlite3OsDelete(pPager->zJournal); - sqliteFree( pPager->aInJournal ); - pPager->aInJournal = 0; - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - pPg->inJournal = 0; - pPg->dirty = 0; - pPg->needSync = 0; - } - pPager->dirtyCache = 0; - pPager->nRec = 0; - }else{ - assert( pPager->dirtyCache==0 || pPager->useJournal==0 ); - } - rc = sqlite3OsUnlock(&pPager->fd, SHARED_LOCK); - pPager->state = PAGER_SHARED; - pPager->origDbSize = 0; - pPager->setMaster = 0; - return rc; -} - -/* -** Compute and return a checksum for the page of data. -** -** This is not a real checksum. It is really just the sum of the -** random initial value and the page number. We experimented with -** a checksum of the entire data, but that was found to be too slow. -** -** Note that the page number is stored at the beginning of data and -** the checksum is stored at the end. This is important. If journal -** corruption occurs due to a power failure, the most likely scenario -** is that one end or the other of the record will be changed. It is -** much less likely that the two ends of the journal record will be -** correct and the middle be corrupt. Thus, this "checksum" scheme, -** though fast and simple, catches the mostly likely kind of corruption. -** -** FIX ME: Consider adding every 200th (or so) byte of the data to the -** checksum. That way if a single page spans 3 or more disk sectors and -** only the middle sector is corrupt, we will still have a reasonable -** chance of failing the checksum and thus detecting the problem. -*/ -static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){ - u32 cksum = pPager->cksumInit; - int i = pPager->pageSize-200; - while( i>0 ){ - cksum += aData[i]; - i -= 200; - } - return cksum; -} - -/* -** Read a single page from the journal file opened on file descriptor -** jfd. Playback this one page. -** -** If useCksum==0 it means this journal does not use checksums. Checksums -** are not used in statement journals because statement journals do not -** need to survive power failures. -*/ -static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ - int rc; - PgHdr *pPg; /* An existing page in the cache */ - Pgno pgno; /* The page number of a page in journal */ - u32 cksum; /* Checksum used for sanity checking */ - u8 aData[SQLITE_MAX_PAGE_SIZE]; /* Temp storage for a page */ - - rc = read32bits(jfd, &pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(jfd, &aData, pPager->pageSize); - if( rc!=SQLITE_OK ) return rc; - pPager->journalOff += pPager->pageSize + 4; - - /* Sanity checking on the page. This is more important that I originally - ** thought. If a power failure occurs while the journal is being written, - ** it could cause invalid data to be written into the journal. We need to - ** detect this invalid data (with high probability) and ignore it. - */ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ - return SQLITE_DONE; - } - if( pgno>(unsigned)pPager->dbSize ){ - return SQLITE_OK; - } - if( useCksum ){ - rc = read32bits(jfd, &cksum); - if( rc ) return rc; - pPager->journalOff += 4; - if( pager_cksum(pPager, pgno, aData)!=cksum ){ - return SQLITE_DONE; - } - } - - assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE ); - - /* If the pager is in RESERVED state, then there must be a copy of this - ** page in the pager cache. In this case just update the pager cache, - ** not the database file. The page is left marked dirty in this case. - ** - ** If in EXCLUSIVE state, then we update the pager cache if it exists - ** and the main file. The page is then marked not dirty. - */ - pPg = pager_lookup(pPager, pgno); - assert( pPager->state>=PAGER_EXCLUSIVE || pPg ); - TRACE3("PLAYBACK %d page %d\n", pPager->fd.h, pgno); - if( pPager->state>=PAGER_EXCLUSIVE ){ - sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); - rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize); - } - if( pPg ){ - /* No page should ever be rolled back that is in use, except for page - ** 1 which is held in use in order to keep the lock on the database - ** active. - */ - void *pData; - assert( pPg->nRef==0 || pPg->pgno==1 ); - pData = PGHDR_TO_DATA(pPg); - memcpy(pData, aData, pPager->pageSize); - if( pPager->xDestructor ){ /*** FIX ME: Should this be xReinit? ***/ - pPager->xDestructor(pData, pPager->pageSize); - } - if( pPager->state>=PAGER_EXCLUSIVE ){ - pPg->dirty = 0; - pPg->needSync = 0; - } - CODEC(pPager, pData, pPg->pgno, 3); - } - return rc; -} - -/* -** Parameter zMaster is the name of a master journal file. A single journal -** file that referred to the master journal file has just been rolled back. -** This routine checks if it is possible to delete the master journal file, -** and does so if it is. -** -** The master journal file contains the names of all child journals. -** To tell if a master journal can be deleted, check to each of the -** children. If all children are either missing or do not refer to -** a different master journal, then this master journal can be deleted. -*/ -static int pager_delmaster(const char *zMaster){ - int rc; - int master_open = 0; - OsFile master; - char *zMasterJournal = 0; /* Contents of master journal file */ - i64 nMasterJournal; /* Size of master journal file */ - - /* Open the master journal file exclusively in case some other process - ** is running this routine also. Not that it makes too much difference. - */ - memset(&master, 0, sizeof(master)); - rc = sqlite3OsOpenReadOnly(zMaster, &master); - if( rc!=SQLITE_OK ) goto delmaster_out; - master_open = 1; - rc = sqlite3OsFileSize(&master, &nMasterJournal); - if( rc!=SQLITE_OK ) goto delmaster_out; - - if( nMasterJournal>0 ){ - char *zJournal; - char *zMasterPtr = 0; - - /* Load the entire master journal file into space obtained from - ** sqliteMalloc() and pointed to by zMasterJournal. - */ - zMasterJournal = (char *)sqliteMalloc(nMasterJournal); - if( !zMasterJournal ){ - rc = SQLITE_NOMEM; - goto delmaster_out; - } - rc = sqlite3OsRead(&master, zMasterJournal, nMasterJournal); - if( rc!=SQLITE_OK ) goto delmaster_out; - - zJournal = zMasterJournal; - while( (zJournal-zMasterJournal)<nMasterJournal ){ - if( sqlite3OsFileExists(zJournal) ){ - /* One of the journals pointed to by the master journal exists. - ** Open it and check if it points at the master journal. If - ** so, return without deleting the master journal file. - */ - OsFile journal; - - memset(&journal, 0, sizeof(journal)); - rc = sqlite3OsOpenReadOnly(zJournal, &journal); - if( rc!=SQLITE_OK ){ - goto delmaster_out; - } - - rc = readMasterJournal(&journal, &zMasterPtr); - sqlite3OsClose(&journal); - if( rc!=SQLITE_OK ){ - goto delmaster_out; - } - - if( zMasterPtr && !strcmp(zMasterPtr, zMaster) ){ - /* We have a match. Do not delete the master journal file. */ - goto delmaster_out; - } - } - zJournal += (strlen(zJournal)+1); - } - } - - sqlite3OsDelete(zMaster); - -delmaster_out: - if( zMasterJournal ){ - sqliteFree(zMasterJournal); - } - if( master_open ){ - sqlite3OsClose(&master); - } - return rc; -} - -/* -** Make every page in the cache agree with what is on disk. In other words, -** reread the disk to reset the state of the cache. -** -** This routine is called after a rollback in which some of the dirty cache -** pages had never been written out to disk. We need to roll back the -** cache content and the easiest way to do that is to reread the old content -** back from the disk. -*/ -static int pager_reload_cache(Pager *pPager){ - PgHdr *pPg; - int rc = SQLITE_OK; - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - char zBuf[SQLITE_MAX_PAGE_SIZE]; - if( !pPg->dirty ) continue; - if( (int)pPg->pgno <= pPager->origDbSize ){ - sqlite3OsSeek(&pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1)); - rc = sqlite3OsRead(&pPager->fd, zBuf, pPager->pageSize); - TRACE3("REFETCH %d page %d\n", pPager->fd.h, pPg->pgno); - if( rc ) break; - CODEC(pPager, zBuf, pPg->pgno, 2); - }else{ - memset(zBuf, 0, pPager->pageSize); - } - if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), pPager->pageSize) ){ - memcpy(PGHDR_TO_DATA(pPg), zBuf, pPager->pageSize); - if( pPager->xReiniter ){ - pPager->xReiniter(PGHDR_TO_DATA(pPg), pPager->pageSize); - }else{ - memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra); - } - } - pPg->needSync = 0; - pPg->dirty = 0; - } - return rc; -} - -/* -** Truncate the main file of the given pager to the number of pages -** indicated. -*/ -static int pager_truncate(Pager *pPager, int nPage){ - return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(i64)nPage); -} - -/* -** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. -** -** The journal file format is as follows: -** -** (1) 8 byte prefix. A copy of aJournalMagic[]. -** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. If this value is 0xffffffff, then compute the -** number of page records from the journal size. -** (3) 4 byte big-endian integer which is the initial value for the -** sanity checksum. -** (4) 4 byte integer which is the number of pages to truncate the -** database to during a rollback. -** (5) 4 byte integer which is the number of bytes in the master journal -** name. The value may be zero (indicate that there is no master -** journal.) -** (6) N bytes of the master journal name. The name will be nul-terminated -** and might be shorter than the value read from (5). If the first byte -** of the name is \000 then there is no master journal. The master -** journal name is stored in UTF-8. -** (7) Zero or more pages instances, each as follows: -** + 4 byte page number. -** + pPager->pageSize bytes of data. -** + 4 byte checksum -** -** When we speak of the journal header, we mean the first 6 items above. -** Each entry in the journal is an instance of the 7th item. -** -** Call the value from the second bullet "nRec". nRec is the number of -** valid page entries in the journal. In most cases, you can compute the -** value of nRec from the size of the journal file. But if a power -** failure occurred while the journal was being written, it could be the -** case that the size of the journal file had already been increased but -** the extra entries had not yet made it safely to disk. In such a case, -** the value of nRec computed from the file size would be too large. For -** that reason, we always use the nRec value in the header. -** -** If the nRec value is 0xffffffff it means that nRec should be computed -** from the file size. This value is used when the user selects the -** no-sync option for the journal. A power failure could lead to corruption -** in this case. But for things like temporary table (which will be -** deleted when the power is restored) we don't care. -** -** If the file opened as the journal file is not a well-formed -** journal file then all pages up to the first corrupted page are rolled -** back (or no pages if the journal header is corrupted). The journal file -** is then deleted and SQLITE_OK returned, just as if no corruption had -** been encountered. -** -** If an I/O or malloc() error occurs, the journal-file is not deleted -** and an error code is returned. -*/ -static int pager_playback(Pager *pPager){ - i64 szJ; /* Size of the journal file in bytes */ - u32 nRec; /* Number of Records in the journal */ - int i; /* Loop counter */ - Pgno mxPg = 0; /* Size of the original file in pages */ - int rc; /* Result code of a subroutine */ - char *zMaster = 0; /* Name of master journal file if any */ - - /* Figure out how many records are in the journal. Abort early if - ** the journal is empty. - */ - assert( pPager->journalOpen ); - rc = sqlite3OsFileSize(&pPager->jfd, &szJ); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - - /* Read the master journal name from the journal, if it is present. - ** If a master journal file name is specified, but the file is not - ** present on disk, then the journal is not hot and does not need to be - ** played back. - */ - rc = readMasterJournal(&pPager->jfd, &zMaster); - assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK || (zMaster && !sqlite3OsFileExists(zMaster)) ){ - sqliteFree(zMaster); - zMaster = 0; - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - goto end_playback; - } - sqlite3OsSeek(&pPager->jfd, 0); - pPager->journalOff = 0; - - /* This loop terminates either when the readJournalHdr() call returns - ** SQLITE_DONE or an IO error occurs. */ - while( 1 ){ - - /* Read the next journal header from the journal file. If there are - ** not enough bytes left in the journal file for a complete header, or - ** it is corrupted, then a process must of failed while writing it. - ** This indicates nothing more needs to be rolled back. - */ - rc = readJournalHdr(pPager, szJ, &nRec, &mxPg); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - } - goto end_playback; - } - - /* If nRec is 0xffffffff, then this journal was created by a process - ** working in no-sync mode. This means that the rest of the journal - ** file consists of pages, there are no more journal headers. Compute - ** the value of nRec based on this assumption. - */ - if( nRec==0xffffffff ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); - nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager); - } - - /* If this is the first header read from the journal, truncate the - ** database file back to it's original size. - */ - if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ - assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg ); - rc = pager_truncate(pPager, mxPg); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - pPager->dbSize = mxPg; - } - - /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */ - if( rc!=SQLITE_OK ) goto end_playback; - - /* Copy original pages out of the journal and back into the database file. - */ - for(i=0; i<nRec; i++){ - rc = pager_playback_one_page(pPager, &pPager->jfd, 1); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - pPager->journalOff = szJ; - break; - }else{ - goto end_playback; - } - } - } - } - - /* Pages that have been written to the journal but never synced - ** where not restored by the loop above. We have to restore those - ** pages by reading them back from the original database. - */ - assert( rc==SQLITE_OK ); - pager_reload_cache(pPager); - -end_playback: - if( rc==SQLITE_OK ){ - rc = pager_unwritelock(pPager); - } - if( zMaster ){ - /* If there was a master journal and this routine will return true, - ** see if it is possible to delete the master journal. If errors - ** occur during this process, ignore them. - */ - if( rc==SQLITE_OK ){ - pager_delmaster(zMaster); - } - sqliteFree(zMaster); - } - - /* The Pager.sectorSize variable may have been updated while rolling - ** back a journal created by a process with a different PAGER_SECTOR_SIZE - ** value. Reset it to the correct value for this process. - */ - pPager->sectorSize = PAGER_SECTOR_SIZE; - return rc; -} - -/* -** Playback the statement journal. -** -** This is similar to playing back the transaction journal but with -** a few extra twists. -** -** (1) The number of pages in the database file at the start of -** the statement is stored in pPager->stmtSize, not in the -** journal file itself. -** -** (2) In addition to playing back the statement journal, also -** playback all pages of the transaction journal beginning -** at offset pPager->stmtJSize. -*/ -static int pager_stmt_playback(Pager *pPager){ - i64 szJ; /* Size of the full journal */ - i64 hdrOff; - int nRec; /* Number of Records */ - int i; /* Loop counter */ - int rc; - - szJ = pPager->journalOff; -#ifndef NDEBUG - { - i64 os_szJ; - rc = sqlite3OsFileSize(&pPager->jfd, &os_szJ); - if( rc!=SQLITE_OK ) return rc; - assert( szJ==os_szJ ); - } -#endif - - /* Set hdrOff to be the offset to the first journal header written - ** this statement transaction, or the end of the file if no journal - ** header was written. - */ - hdrOff = pPager->stmtHdrOff; - assert( pPager->fullSync || !hdrOff ); - if( !hdrOff ){ - hdrOff = szJ; - } - - - /* Truncate the database back to its original size. - */ - rc = pager_truncate(pPager, pPager->stmtSize); - pPager->dbSize = pPager->stmtSize; - - /* Figure out how many records are in the statement journal. - */ - assert( pPager->stmtInUse && pPager->journalOpen ); - sqlite3OsSeek(&pPager->stfd, 0); - nRec = pPager->stmtNRec; - - /* Copy original pages out of the statement journal and back into the - ** database file. Note that the statement journal omits checksums from - ** each record since power-failure recovery is not important to statement - ** journals. - */ - for(i=nRec-1; i>=0; i--){ - rc = pager_playback_one_page(pPager, &pPager->stfd, 0); - assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK ) goto end_stmt_playback; - } - - /* Now roll some pages back from the transaction journal. Pager.stmtJSize - ** was the size of the journal file when this statement was started, so - ** everything after that needs to be rolled back, either into the - ** database, the memory cache, or both. - ** - ** If it is not zero, then Pager.stmtHdrOff is the offset to the start - ** of the first journal header written during this statement transaction. - */ - rc = sqlite3OsSeek(&pPager->jfd, pPager->stmtJSize); - if( rc!=SQLITE_OK ){ - goto end_stmt_playback; - } - pPager->journalOff = pPager->stmtJSize; - pPager->cksumInit = pPager->stmtCksum; - assert( JOURNAL_HDR_SZ(pPager)<(pPager->pageSize+8) ); - while( pPager->journalOff <= (hdrOff-(pPager->pageSize+8)) ){ - rc = pager_playback_one_page(pPager, &pPager->jfd, 1); - assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK ) goto end_stmt_playback; - } - - while( pPager->journalOff < szJ ){ - u32 nRec; - u32 dummy; - rc = readJournalHdr(pPager, szJ, &nRec, &dummy); - if( rc!=SQLITE_OK ){ - assert( rc!=SQLITE_DONE ); - goto end_stmt_playback; - } - if( nRec==0 ){ - nRec = (szJ - pPager->journalOff) / (pPager->pageSize+8); - } - for(i=nRec-1; i>=0 && pPager->journalOff < szJ; i--){ - rc = pager_playback_one_page(pPager, &pPager->jfd, 1); - assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK ) goto end_stmt_playback; - } - } - - pPager->journalOff = szJ; - -end_stmt_playback: - if( rc!=SQLITE_OK ){ - pPager->errMask |= PAGER_ERR_CORRUPT; - rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */ - }else{ - pPager->journalOff = szJ; - /* pager_reload_cache(pPager); */ - } - return rc; -} - -/* -** Change the maximum number of in-memory pages that are allowed. -** -** The maximum number is the absolute value of the mxPage parameter. -** If mxPage is negative, the noSync flag is also set. noSync bypasses -** calls to sqlite3OsSync(). The pager runs much faster with noSync on, -** but if the operating system crashes or there is an abrupt power -** failure, the database file might be left in an inconsistent and -** unrepairable state. -*/ -void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ - if( mxPage>=0 ){ - pPager->noSync = pPager->tempFile; - if( pPager->noSync ) pPager->needSync = 0; - }else{ - pPager->noSync = 1; - mxPage = -mxPage; - } - if( mxPage>10 ){ - pPager->mxPage = mxPage; - }else{ - pPager->mxPage = 10; - } -} - -/* -** Adjust the robustness of the database to damage due to OS crashes -** or power failures by changing the number of syncs()s when writing -** the rollback journal. There are three levels: -** -** OFF sqlite3OsSync() is never called. This is the default -** for temporary and transient files. -** -** NORMAL The journal is synced once before writes begin on the -** database. This is normally adequate protection, but -** it is theoretically possible, though very unlikely, -** that an inopertune power failure could leave the journal -** in a state which would cause damage to the database -** when it is rolled back. -** -** FULL The journal is synced twice before writes begin on the -** database (with some additional information - the nRec field -** of the journal header - being written in between the two -** syncs). If we assume that writing a -** single disk sector is atomic, then this mode provides -** assurance that the journal will not be corrupted to the -** point of causing damage to the database during rollback. -** -** Numeric values associated with these states are OFF==1, NORMAL=2, -** and FULL=3. -*/ -void sqlite3pager_set_safety_level(Pager *pPager, int level){ - pPager->noSync = level==1 || pPager->tempFile; - pPager->fullSync = level==3 && !pPager->tempFile; - if( pPager->noSync ) pPager->needSync = 0; -} - -/* -** Open a temporary file. Write the name of the file into zName -** (zName must be at least SQLITE_TEMPNAME_SIZE bytes long.) Write -** the file descriptor into *fd. Return SQLITE_OK on success or some -** other error code if we fail. -** -** The OS will automatically delete the temporary file when it is -** closed. -*/ -static int sqlite3pager_opentemp(char *zFile, OsFile *fd){ - int cnt = 8; - int rc; - do{ - cnt--; - sqlite3OsTempFileName(zFile); - rc = sqlite3OsOpenExclusive(zFile, fd, 1); - }while( cnt>0 && rc!=SQLITE_OK && rc!=SQLITE_NOMEM ); - return rc; -} - -/* -** Create a new page cache and put a pointer to the page cache in *ppPager. -** The file to be cached need not exist. The file is not locked until -** the first call to sqlite3pager_get() and is only held open until the -** last page is released using sqlite3pager_unref(). -** -** If zFilename is NULL then a randomly-named temporary file is created -** and used as the file to be cached. The file will be deleted -** automatically when it is closed. -** -** If zFilename is ":memory:" then all information is held in cache. -** It is never written to disk. This can be used to implement an -** in-memory database. -*/ -int sqlite3pager_open( - Pager **ppPager, /* Return the Pager structure here */ - const char *zFilename, /* Name of the database file to open */ - int nExtra, /* Extra bytes append to each in-memory page */ - int useJournal /* TRUE to use a rollback journal on this file */ -){ - Pager *pPager; - char *zFullPathname = 0; - int nameLen; - OsFile fd; - int rc = SQLITE_OK; - int i; - int tempFile = 0; - int memDb = 0; - int readOnly = 0; - char zTemp[SQLITE_TEMPNAME_SIZE]; - - *ppPager = 0; - memset(&fd, 0, sizeof(fd)); - if( sqlite3_malloc_failed ){ - return SQLITE_NOMEM; - } - if( zFilename && zFilename[0] ){ - if( strcmp(zFilename,":memory:")==0 ){ - memDb = 1; - zFullPathname = sqliteStrDup(""); - rc = SQLITE_OK; - }else{ - zFullPathname = sqlite3OsFullPathname(zFilename); - if( zFullPathname ){ - rc = sqlite3OsOpenReadWrite(zFullPathname, &fd, &readOnly); - } - } - }else{ - rc = sqlite3pager_opentemp(zTemp, &fd); - zFilename = zTemp; - zFullPathname = sqlite3OsFullPathname(zFilename); - if( rc==SQLITE_OK ){ - tempFile = 1; - } - } - if( !zFullPathname ){ - sqlite3OsClose(&fd); - return SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&fd); - sqliteFree(zFullPathname); - return rc; - } - nameLen = strlen(zFullPathname); - pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); - if( pPager==0 ){ - sqlite3OsClose(&fd); - sqliteFree(zFullPathname); - return SQLITE_NOMEM; - } - TRACE3("OPEN %d %s\n", fd.h, zFullPathname); - pPager->zFilename = (char*)&pPager[1]; - pPager->zDirectory = &pPager->zFilename[nameLen+1]; - pPager->zJournal = &pPager->zDirectory[nameLen+1]; - strcpy(pPager->zFilename, zFullPathname); - strcpy(pPager->zDirectory, zFullPathname); - for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){} - if( i>0 ) pPager->zDirectory[i-1] = 0; - strcpy(pPager->zJournal, zFullPathname); - sqliteFree(zFullPathname); - strcpy(&pPager->zJournal[nameLen], "-journal"); - pPager->fd = fd; -#if OS_UNIX - pPager->fd.pPager = pPager; -#endif - pPager->journalOpen = 0; - pPager->useJournal = useJournal && !memDb; - pPager->stmtOpen = 0; - pPager->stmtInUse = 0; - pPager->nRef = 0; - pPager->dbSize = memDb-1; - pPager->pageSize = SQLITE_DEFAULT_PAGE_SIZE; - pPager->stmtSize = 0; - pPager->stmtJSize = 0; - pPager->nPage = 0; - pPager->mxPage = 100; - pPager->state = PAGER_UNLOCK; - pPager->errMask = 0; - pPager->tempFile = tempFile; - pPager->memDb = memDb; - pPager->readOnly = readOnly; - pPager->needSync = 0; - pPager->noSync = pPager->tempFile || !useJournal; - pPager->fullSync = (pPager->noSync?0:1); - pPager->pFirst = 0; - pPager->pFirstSynced = 0; - pPager->pLast = 0; - pPager->nExtra = nExtra; - pPager->sectorSize = PAGER_SECTOR_SIZE; - pPager->pBusyHandler = 0; - memset(pPager->aHash, 0, sizeof(pPager->aHash)); - *ppPager = pPager; - return SQLITE_OK; -} - -/* -** Set the busy handler function. -*/ -void sqlite3pager_set_busyhandler(Pager *pPager, BusyHandler *pBusyHandler){ - pPager->pBusyHandler = pBusyHandler; -} - -/* -** Set the destructor for this pager. If not NULL, the destructor is called -** when the reference count on each page reaches zero. The destructor can -** be used to clean up information in the extra segment appended to each page. -** -** The destructor is not called as a result sqlite3pager_close(). -** Destructors are only called by sqlite3pager_unref(). -*/ -void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*,int)){ - pPager->xDestructor = xDesc; -} - -/* -** Set the reinitializer for this pager. If not NULL, the reinitializer -** is called when the content of a page in cache is restored to its original -** value as a result of a rollback. The callback gives higher-level code -** an opportunity to restore the EXTRA section to agree with the restored -** page data. -*/ -void sqlite3pager_set_reiniter(Pager *pPager, void (*xReinit)(void*,int)){ - pPager->xReiniter = xReinit; -} - -/* -** Set the page size. -** -** The page size must only be changed when the cache is empty. -*/ -void sqlite3pager_set_pagesize(Pager *pPager, int pageSize){ - assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ); - pPager->pageSize = pageSize; -} - -/* -** Read the first N bytes from the beginning of the file into memory -** that pDest points to. No error checking is done. -*/ -void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){ - memset(pDest, 0, N); - if( pPager->memDb==0 ){ - sqlite3OsSeek(&pPager->fd, 0); - sqlite3OsRead(&pPager->fd, pDest, N); - } -} - -/* -** Return the total number of pages in the disk file associated with -** pPager. -*/ -int sqlite3pager_pagecount(Pager *pPager){ - i64 n; - assert( pPager!=0 ); - if( pPager->dbSize>=0 ){ - return pPager->dbSize; - } - if( sqlite3OsFileSize(&pPager->fd, &n)!=SQLITE_OK ){ - pPager->errMask |= PAGER_ERR_DISK; - return 0; - } - n /= pPager->pageSize; - if( !pPager->memDb && n==PENDING_BYTE/pPager->pageSize ){ - n++; - } - if( pPager->state!=PAGER_UNLOCK ){ - pPager->dbSize = n; - } - return n; -} - -/* -** Forward declaration -*/ -static int syncJournal(Pager*); - - -/* -** Unlink a page from the free list (the list of all pages where nRef==0) -** and from its hash collision chain. -*/ -static void unlinkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - - /* Keep the pFirstSynced pointer pointing at the first synchronized page */ - if( pPg==pPager->pFirstSynced ){ - PgHdr *p = pPg->pNextFree; - while( p && p->needSync ){ p = p->pNextFree; } - pPager->pFirstSynced = p; - } - - /* Unlink from the freelist */ - if( pPg->pPrevFree ){ - pPg->pPrevFree->pNextFree = pPg->pNextFree; - }else{ - assert( pPager->pFirst==pPg ); - pPager->pFirst = pPg->pNextFree; - } - if( pPg->pNextFree ){ - pPg->pNextFree->pPrevFree = pPg->pPrevFree; - }else{ - assert( pPager->pLast==pPg ); - pPager->pLast = pPg->pPrevFree; - } - pPg->pNextFree = pPg->pPrevFree = 0; - - /* Unlink from the pgno hash table */ - if( pPg->pNextHash ){ - pPg->pNextHash->pPrevHash = pPg->pPrevHash; - } - if( pPg->pPrevHash ){ - pPg->pPrevHash->pNextHash = pPg->pNextHash; - }else{ - int h = pager_hash(pPg->pgno); - assert( pPager->aHash[h]==pPg ); - pPager->aHash[h] = pPg->pNextHash; - } - pPg->pNextHash = pPg->pPrevHash = 0; -} - -/* -** This routine is used to truncate an in-memory database. Delete -** all pages whose pgno is larger than pPager->dbSize and is unreferenced. -** Referenced pages larger than pPager->dbSize are zeroed. -*/ -static void memoryTruncate(Pager *pPager){ - PgHdr *pPg; - PgHdr **ppPg; - int dbSize = pPager->dbSize; - - ppPg = &pPager->pAll; - while( (pPg = *ppPg)!=0 ){ - if( pPg->pgno<=dbSize ){ - ppPg = &pPg->pNextAll; - }else if( pPg->nRef>0 ){ - memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); - ppPg = &pPg->pNextAll; - }else{ - *ppPg = pPg->pNextAll; - unlinkPage(pPg); - sqliteFree(pPg); - pPager->nPage--; - } - } -} - -/* -** Truncate the file to the number of pages specified. -*/ -int sqlite3pager_truncate(Pager *pPager, Pgno nPage){ - int rc; - sqlite3pager_pagecount(pPager); - if( pPager->errMask!=0 ){ - rc = pager_errcode(pPager); - return rc; - } - if( nPage>=(unsigned)pPager->dbSize ){ - return SQLITE_OK; - } - if( pPager->memDb ){ - pPager->dbSize = nPage; - memoryTruncate(pPager); - return SQLITE_OK; - } - rc = syncJournal(pPager); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = pager_truncate(pPager, nPage); - if( rc==SQLITE_OK ){ - pPager->dbSize = nPage; - } - return rc; -} - -/* -** Shutdown the page cache. Free all memory and close all files. -** -** If a transaction was in progress when this routine is called, that -** transaction is rolled back. All outstanding pages are invalidated -** and their memory is freed. Any attempt to use a page associated -** with this page cache after this function returns will likely -** result in a coredump. -*/ -int sqlite3pager_close(Pager *pPager){ - PgHdr *pPg, *pNext; - switch( pPager->state ){ - case PAGER_RESERVED: - case PAGER_SYNCED: - case PAGER_EXCLUSIVE: { - sqlite3pager_rollback(pPager); - if( !pPager->memDb ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - } - assert( pPager->journalOpen==0 ); - break; - } - case PAGER_SHARED: { - if( !pPager->memDb ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - } - break; - } - default: { - /* Do nothing */ - break; - } - } - for(pPg=pPager->pAll; pPg; pPg=pNext){ -#ifndef NDEBUG - if( pPager->memDb ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - assert( !pPg->alwaysRollback ); - assert( !pHist->pOrig ); - assert( !pHist->pStmt ); - } -#endif - pNext = pPg->pNextAll; - sqliteFree(pPg); - } - TRACE2("CLOSE %d\n", pPager->fd.h); - sqlite3OsClose(&pPager->fd); - assert( pPager->journalOpen==0 ); - /* Temp files are automatically deleted by the OS - ** if( pPager->tempFile ){ - ** sqlite3OsDelete(pPager->zFilename); - ** } - */ - if( pPager->zFilename!=(char*)&pPager[1] ){ - assert( 0 ); /* Cannot happen */ - sqliteFree(pPager->zFilename); - sqliteFree(pPager->zJournal); - sqliteFree(pPager->zDirectory); - } - sqliteFree(pPager); - return SQLITE_OK; -} - -/* -** Return the page number for the given page data. -*/ -Pgno sqlite3pager_pagenumber(void *pData){ - PgHdr *p = DATA_TO_PGHDR(pData); - return p->pgno; -} - -/* -** The page_ref() function increments the reference count for a page. -** If the page is currently on the freelist (the reference count is zero) then -** remove it from the freelist. -** -** For non-test systems, page_ref() is a macro that calls _page_ref() -** online of the reference count is zero. For test systems, page_ref() -** is a real function so that we can set breakpoints and trace it. -*/ -static void _page_ref(PgHdr *pPg){ - if( pPg->nRef==0 ){ - /* The page is currently on the freelist. Remove it. */ - if( pPg==pPg->pPager->pFirstSynced ){ - PgHdr *p = pPg->pNextFree; - while( p && p->needSync ){ p = p->pNextFree; } - pPg->pPager->pFirstSynced = p; - } - if( pPg->pPrevFree ){ - pPg->pPrevFree->pNextFree = pPg->pNextFree; - }else{ - pPg->pPager->pFirst = pPg->pNextFree; - } - if( pPg->pNextFree ){ - pPg->pNextFree->pPrevFree = pPg->pPrevFree; - }else{ - pPg->pPager->pLast = pPg->pPrevFree; - } - pPg->pPager->nRef++; - } - pPg->nRef++; - REFINFO(pPg); -} -#ifdef SQLITE_TEST - static void page_ref(PgHdr *pPg){ - if( pPg->nRef==0 ){ - _page_ref(pPg); - }else{ - pPg->nRef++; - REFINFO(pPg); - } - } -#else -# define page_ref(P) ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++) -#endif - -/* -** Increment the reference count for a page. The input pointer is -** a reference to the page data. -*/ -int sqlite3pager_ref(void *pData){ - PgHdr *pPg = DATA_TO_PGHDR(pData); - page_ref(pPg); - return SQLITE_OK; -} - -/* -** Sync the journal. In other words, make sure all the pages that have -** been written to the journal have actually reached the surface of the -** disk. It is not safe to modify the original database file until after -** the journal has been synced. If the original database is modified before -** the journal is synced and a power failure occurs, the unsynced journal -** data would be lost and we would be unable to completely rollback the -** database changes. Database corruption would occur. -** -** This routine also updates the nRec field in the header of the journal. -** (See comments on the pager_playback() routine for additional information.) -** If the sync mode is FULL, two syncs will occur. First the whole journal -** is synced, then the nRec field is updated, then a second sync occurs. -** -** For temporary databases, we do not care if we are able to rollback -** after a power failure, so sync occurs. -** -** This routine clears the needSync field of every page current held in -** memory. -*/ -static int syncJournal(Pager *pPager){ - PgHdr *pPg; - int rc = SQLITE_OK; - - /* Sync the journal before modifying the main database - ** (assuming there is a journal and it needs to be synced.) - */ - if( pPager->needSync ){ - if( !pPager->tempFile ){ - assert( pPager->journalOpen ); - /* assert( !pPager->noSync ); // noSync might be set if synchronous - ** was turned off after the transaction was started. Ticket #615 */ -#ifndef NDEBUG - { - /* Make sure the pPager->nRec counter we are keeping agrees - ** with the nRec computed from the size of the journal file. - */ - i64 jSz; - rc = sqlite3OsFileSize(&pPager->jfd, &jSz); - if( rc!=0 ) return rc; - assert( pPager->journalOff==jSz ); - } -#endif - { - /* Write the nRec value into the journal file header. If in - ** full-synchronous mode, sync the journal first. This ensures that - ** all data has really hit the disk before nRec is updated to mark - ** it as a candidate for rollback. - */ - if( pPager->fullSync ){ - TRACE2("SYNC journal of %d\n", pPager->fd.h); - rc = sqlite3OsSync(&pPager->jfd); - if( rc!=0 ) return rc; - } - sqlite3OsSeek(&pPager->jfd, pPager->journalHdr + sizeof(aJournalMagic)); - rc = write32bits(&pPager->jfd, pPager->nRec); - if( rc ) return rc; - - sqlite3OsSeek(&pPager->jfd, pPager->journalOff); - } - TRACE2("SYNC journal of %d\n", pPager->fd.h); - rc = sqlite3OsSync(&pPager->jfd); - if( rc!=0 ) return rc; - pPager->journalStarted = 1; - } - pPager->needSync = 0; - - /* Erase the needSync flag from every page. - */ - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - pPg->needSync = 0; - } - pPager->pFirstSynced = pPager->pFirst; - } - -#ifndef NDEBUG - /* If the Pager.needSync flag is clear then the PgHdr.needSync - ** flag must also be clear for all pages. Verify that this - ** invariant is true. - */ - else{ - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - assert( pPg->needSync==0 ); - } - assert( pPager->pFirstSynced==pPager->pFirst ); - } -#endif - - return rc; -} - -/* -** Try to obtain a lock on a file. Invoke the busy callback if the lock -** is currently not available. Repeate until the busy callback returns -** false or until the lock succeeds. -** -** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. -*/ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; - assert( PAGER_SHARED==SHARED_LOCK ); - assert( PAGER_RESERVED==RESERVED_LOCK ); - assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); - if( pPager->state>=locktype ){ - rc = SQLITE_OK; - }else{ - int busy = 1; - do { - rc = sqlite3OsLock(&pPager->fd, locktype); - }while( rc==SQLITE_BUSY && - pPager->pBusyHandler && - pPager->pBusyHandler->xFunc && - pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++) - ); - if( rc==SQLITE_OK ){ - pPager->state = locktype; - } - } - return rc; -} - -/* -** Given a list of pages (connected by the PgHdr.pDirty pointer) write -** every one of those pages out to the database file and mark them all -** as clean. -*/ -static int pager_write_pagelist(PgHdr *pList){ - Pager *pPager; - int rc; - - if( pList==0 ) return SQLITE_OK; - pPager = pList->pPager; - - /* At this point there may be either a RESERVED or EXCLUSIVE lock on the - ** database file. If there is already an EXCLUSIVE lock, the following - ** calls to sqlite3OsLock() are no-ops. - ** - ** Moving the lock from RESERVED to EXCLUSIVE actually involves going - ** through an intermediate state PENDING. A PENDING lock prevents new - ** readers from attaching to the database but is unsufficient for us to - ** write. The idea of a PENDING lock is to prevent new readers from - ** coming in while we wait for existing readers to clear. - ** - ** While the pager is in the RESERVED state, the original database file - ** is unchanged and we can rollback without having to playback the - ** journal into the original database file. Once we transition to - ** EXCLUSIVE, it means the database file has been changed and any rollback - ** will require a journal playback. - */ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - return rc; - } - - while( pList ){ - assert( pList->dirty ); - sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize); - CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); - TRACE3("STORE %d page %d\n", pPager->fd.h, pList->pgno); - rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize); - CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0); - if( rc ) return rc; - pList->dirty = 0; - pList = pList->pDirty; - } - return SQLITE_OK; -} - -/* -** Collect every dirty page into a dirty list and -** return a pointer to the head of that list. All pages are -** collected even if they are still in use. -*/ -static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ - PgHdr *p, *pList; - pList = 0; - for(p=pPager->pAll; p; p=p->pNextAll){ - if( p->dirty ){ - p->pDirty = pList; - pList = p; - } - } - return pList; -} - -/* -** Acquire a page. -** -** A read lock on the disk file is obtained when the first page is acquired. -** This read lock is dropped when the last page is released. -** -** A _get works for any page number greater than 0. If the database -** file is smaller than the requested page, then no actual disk -** read occurs and the memory image of the page is initialized to -** all zeros. The extra data appended to a page is always initialized -** to zeros the first time a page is loaded into memory. -** -** The acquisition might fail for several reasons. In all cases, -** an appropriate error code is returned and *ppPage is set to NULL. -** -** See also sqlite3pager_lookup(). Both this routine and _lookup() attempt -** to find a page in the in-memory cache first. If the page is not already -** in memory, this routine goes to disk to read it in whereas _lookup() -** just returns 0. This routine acquires a read-lock the first time it -** has to go to disk, and could also playback an old journal if necessary. -** Since _lookup() never goes to disk, it never has to deal with locks -** or journal files. -*/ -int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ - PgHdr *pPg; - int rc; - - /* Make sure we have not hit any critical errors. - */ - assert( pPager!=0 ); - assert( pgno!=0 ); - *ppPage = 0; - if( pPager->errMask & ~(PAGER_ERR_FULL) ){ - return pager_errcode(pPager); - } - - /* If this is the first page accessed, then get a SHARED lock - ** on the database file. - */ - if( pPager->nRef==0 && !pPager->memDb ){ - rc = pager_wait_on_lock(pPager, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* If a journal file exists, and there is no RESERVED lock on the - ** database file, then it either needs to be played back or deleted. - */ - if( pPager->useJournal && - sqlite3OsFileExists(pPager->zJournal) && - !sqlite3OsCheckReservedLock(&pPager->fd) - ){ - int rc; - - /* Get an EXCLUSIVE lock on the database file. At this point it is - ** important that a RESERVED lock is not obtained on the way to the - ** EXCLUSIVE lock. If it were, another process might open the - ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling it - ** back. - ** - ** Because the intermediate RESERVED lock is not requested, the - ** second process will get to this point in the code and fail to - ** obtain it's own EXCLUSIVE lock on the database file. - */ - rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - pPager->state = PAGER_UNLOCK; - return rc; - } - pPager->state = PAGER_EXCLUSIVE; - - /* Open the journal for reading only. Return SQLITE_BUSY if - ** we are unable to open the journal file. - ** - ** The journal file does not need to be locked itself. The - ** journal file is never open unless the main database file holds - ** a write lock, so there is never any chance of two or more - ** processes opening the journal at the same time. - */ - rc = sqlite3OsOpenReadOnly(pPager->zJournal, &pPager->jfd); - if( rc!=SQLITE_OK ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - pPager->state = PAGER_UNLOCK; - return SQLITE_BUSY; - } - pPager->journalOpen = 1; - pPager->journalStarted = 0; - pPager->journalOff = 0; - pPager->setMaster = 0; - pPager->journalHdr = 0; - - /* Playback and delete the journal. Drop the database write - ** lock and reacquire the read lock. - */ - rc = pager_playback(pPager); - if( rc!=SQLITE_OK ){ - return rc; - } - } - pPg = 0; - }else{ - /* Search for page in cache */ - pPg = pager_lookup(pPager, pgno); - if( pPager->memDb && pPager->state==PAGER_UNLOCK ){ - pPager->state = PAGER_SHARED; - } - } - if( pPg==0 ){ - /* The requested page is not in the page cache. */ - int h; - pPager->nMiss++; - if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 || pPager->memDb ){ - /* Create a new page */ - pPg = sqliteMallocRaw( sizeof(*pPg) + pPager->pageSize - + sizeof(u32) + pPager->nExtra - + pPager->memDb*sizeof(PgHistory) ); - if( pPg==0 ){ - if( !pPager->memDb ){ - pager_unwritelock(pPager); - } - pPager->errMask |= PAGER_ERR_MEM; - return SQLITE_NOMEM; - } - memset(pPg, 0, sizeof(*pPg)); - if( pPager->memDb ){ - memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory)); - } - pPg->pPager = pPager; - pPg->pNextAll = pPager->pAll; - pPager->pAll = pPg; - pPager->nPage++; - }else{ - /* Find a page to recycle. Try to locate a page that does not - ** require us to do an fsync() on the journal. - */ - pPg = pPager->pFirstSynced; - - /* If we could not find a page that does not require an fsync() - ** on the journal file then fsync the journal file. This is a - ** very slow operation, so we work hard to avoid it. But sometimes - ** it can't be helped. - */ - if( pPg==0 ){ - int rc = syncJournal(pPager); - if( rc!=0 ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; - } - if( pPager->fullSync ){ - /* If in full-sync mode, write a new journal header into the - ** journal file. This is done to avoid ever modifying a journal - ** header that is involved in the rollback of pages that have - ** already been written to the database (in case the header is - ** trashed when the nRec field is updated). - */ - pPager->nRec = 0; - assert( pPager->journalOff > 0 ); - rc = writeJournalHdr(pPager); - if( rc!=0 ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; - } - } - pPg = pPager->pFirst; - } - assert( pPg->nRef==0 ); - - /* Write the page to the database file if it is dirty. - */ - if( pPg->dirty ){ - assert( pPg->needSync==0 ); - pPg->pDirty = 0; - rc = pager_write_pagelist( pPg ); - if( rc!=SQLITE_OK ){ - sqlite3pager_rollback(pPager); - return SQLITE_IOERR; - } - } - assert( pPg->dirty==0 ); - - /* If the page we are recycling is marked as alwaysRollback, then - ** set the global alwaysRollback flag, thus disabling the - ** sqlite_dont_rollback() optimization for the rest of this transaction. - ** It is necessary to do this because the page marked alwaysRollback - ** might be reloaded at a later time but at that point we won't remember - ** that is was marked alwaysRollback. This means that all pages must - ** be marked as alwaysRollback from here on out. - */ - if( pPg->alwaysRollback ){ - pPager->alwaysRollback = 1; - } - - /* Unlink the old page from the free list and the hash table - */ - unlinkPage(pPg); - pPager->nOvfl++; - } - pPg->pgno = pgno; - if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){ - sqlite3CheckMemory(pPager->aInJournal, pgno/8); - assert( pPager->journalOpen ); - pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0; - pPg->needSync = 0; - }else{ - pPg->inJournal = 0; - pPg->needSync = 0; - } - if( pPager->aInStmt && (int)pgno<=pPager->stmtSize - && (pPager->aInStmt[pgno/8] & (1<<(pgno&7)))!=0 ){ - page_add_to_stmt_list(pPg); - }else{ - page_remove_from_stmt_list(pPg); - } - pPg->dirty = 0; - pPg->nRef = 1; - REFINFO(pPg); - pPager->nRef++; - h = pager_hash(pgno); - pPg->pNextHash = pPager->aHash[h]; - pPager->aHash[h] = pPg; - if( pPg->pNextHash ){ - assert( pPg->pNextHash->pPrevHash==0 ); - pPg->pNextHash->pPrevHash = pPg; - } - if( pPager->nExtra>0 ){ - memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra); - } - sqlite3pager_pagecount(pPager); - if( pPager->errMask!=0 ){ - sqlite3pager_unref(PGHDR_TO_DATA(pPg)); - rc = pager_errcode(pPager); - return rc; - } - if( pPager->dbSize<(int)pgno ){ - memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); - }else{ - int rc; - assert( pPager->memDb==0 ); - sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); - rc = sqlite3OsRead(&pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); - TRACE3("FETCH %d page %d\n", pPager->fd.h, pPg->pgno); - CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); - if( rc!=SQLITE_OK ){ - i64 fileSize; - if( sqlite3OsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK - || fileSize>=pgno*pPager->pageSize ){ - sqlite3pager_unref(PGHDR_TO_DATA(pPg)); - return rc; - }else{ - memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); - } - } - } - }else{ - /* The requested page is in the page cache. */ - pPager->nHit++; - page_ref(pPg); - } - *ppPage = PGHDR_TO_DATA(pPg); - return SQLITE_OK; -} - -/* -** Acquire a page if it is already in the in-memory cache. Do -** not read the page from disk. Return a pointer to the page, -** or 0 if the page is not in cache. -** -** See also sqlite3pager_get(). The difference between this routine -** and sqlite3pager_get() is that _get() will go to the disk and read -** in the page if the page is not already in cache. This routine -** returns NULL if the page is not in cache or if a disk I/O error -** has ever happened. -*/ -void *sqlite3pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *pPg; - - assert( pPager!=0 ); - assert( pgno!=0 ); - if( pPager->errMask & ~(PAGER_ERR_FULL) ){ - return 0; - } - pPg = pager_lookup(pPager, pgno); - if( pPg==0 ) return 0; - page_ref(pPg); - return PGHDR_TO_DATA(pPg); -} - -/* -** Release a page. -** -** If the number of references to the page drop to zero, then the -** page is added to the LRU list. When all references to all pages -** are released, a rollback occurs and the lock on the database is -** removed. -*/ -int sqlite3pager_unref(void *pData){ - PgHdr *pPg; - - /* Decrement the reference count for this page - */ - pPg = DATA_TO_PGHDR(pData); - assert( pPg->nRef>0 ); - pPg->nRef--; - REFINFO(pPg); - - /* When the number of references to a page reach 0, call the - ** destructor and add the page to the freelist. - */ - if( pPg->nRef==0 ){ - Pager *pPager; - pPager = pPg->pPager; - pPg->pNextFree = 0; - pPg->pPrevFree = pPager->pLast; - pPager->pLast = pPg; - if( pPg->pPrevFree ){ - pPg->pPrevFree->pNextFree = pPg; - }else{ - pPager->pFirst = pPg; - } - if( pPg->needSync==0 && pPager->pFirstSynced==0 ){ - pPager->pFirstSynced = pPg; - } - if( pPager->xDestructor ){ - pPager->xDestructor(pData, pPager->pageSize); - } - - /* When all pages reach the freelist, drop the read lock from - ** the database file. - */ - pPager->nRef--; - assert( pPager->nRef>=0 ); - if( pPager->nRef==0 && !pPager->memDb ){ - pager_reset(pPager); - } - } - return SQLITE_OK; -} - -/* -** Create a journal file for pPager. There should already be a RESERVED -** or EXCLUSIVE lock on the database file when this routine is called. -** -** Return SQLITE_OK if everything. Return an error code and release the -** write lock if anything goes wrong. -*/ -static int pager_open_journal(Pager *pPager){ - int rc; - assert( !pPager->memDb ); - assert( pPager->state>=PAGER_RESERVED ); - assert( pPager->journalOpen==0 ); - assert( pPager->useJournal ); - sqlite3pager_pagecount(pPager); - pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 ); - if( pPager->aInJournal==0 ){ - rc = SQLITE_NOMEM; - goto failed_to_open_journal; - } - rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile); - pPager->journalOff = 0; - pPager->setMaster = 0; - pPager->journalHdr = 0; - if( rc!=SQLITE_OK ){ - goto failed_to_open_journal; - } - sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd); - pPager->journalOpen = 1; - pPager->journalStarted = 0; - pPager->needSync = 0; - pPager->alwaysRollback = 0; - pPager->nRec = 0; - if( pPager->errMask!=0 ){ - rc = pager_errcode(pPager); - return rc; - } - pPager->origDbSize = pPager->dbSize; - - rc = writeJournalHdr(pPager); - - if( pPager->stmtAutoopen && rc==SQLITE_OK ){ - rc = sqlite3pager_stmt_begin(pPager); - } - if( rc!=SQLITE_OK ){ - rc = pager_unwritelock(pPager); - if( rc==SQLITE_OK ){ - rc = SQLITE_FULL; - } - } - return rc; - -failed_to_open_journal: - sqliteFree(pPager->aInJournal); - pPager->aInJournal = 0; - sqlite3OsUnlock(&pPager->fd, NO_LOCK); - pPager->state = PAGER_UNLOCK; - return rc; -} - -/* -** Acquire a write-lock on the database. The lock is removed when -** the any of the following happen: -** -** * sqlite3pager_commit() is called. -** * sqlite3pager_rollback() is called. -** * sqlite3pager_close() is called. -** * sqlite3pager_unref() is called to on every outstanding page. -** -** The first parameter to this routine is a pointer to any open page of the -** database file. Nothing changes about the page - it is used merely to -** acquire a pointer to the Pager structure and as proof that there is -** already a read-lock on the database. -** -** The second parameter indicates how much space in bytes to reserve for a -** master journal file-name at the start of the journal when it is created. -** -** A journal file is opened if this is not a temporary file. For temporary -** files, the opening of the journal file is deferred until there is an -** actual need to write to the journal. -** -** If the database is already reserved for writing, this routine is a no-op. -** -** If exFlag is true, go ahead and get an EXCLUSIVE lock on the file -** immediately instead of waiting until we try to flush the cache. The -** exFlag is ignored if a transaction is already active. -*/ -int sqlite3pager_begin(void *pData, int exFlag){ - PgHdr *pPg = DATA_TO_PGHDR(pData); - Pager *pPager = pPg->pPager; - int rc = SQLITE_OK; - assert( pPg->nRef>0 ); - assert( pPager->state!=PAGER_UNLOCK ); - if( pPager->state==PAGER_SHARED ){ - assert( pPager->aInJournal==0 ); - if( pPager->memDb ){ - pPager->state = PAGER_EXCLUSIVE; - pPager->origDbSize = pPager->dbSize; - }else{ - if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){ - rc = pager_wait_on_lock(pPager, RESERVED_LOCK); - }else{ - rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK); - } - if( rc==SQLITE_OK ){ - pPager->state = PAGER_RESERVED; - if( exFlag ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - } - if( rc!=SQLITE_OK ){ - return rc; - } - pPager->dirtyCache = 0; - TRACE2("TRANSACTION %d\n", pPager->fd.h); - if( pPager->useJournal && !pPager->tempFile ){ - rc = pager_open_journal(pPager); - } - } - } - return rc; -} - -/* -** Mark a data page as writeable. The page is written into the journal -** if it is not there already. This routine must be called before making -** changes to a page. -** -** The first time this routine is called, the pager creates a new -** journal and acquires a RESERVED lock on the database. If the RESERVED -** lock could not be acquired, this routine returns SQLITE_BUSY. The -** calling routine must check for that return value and be careful not to -** change any page data until this routine returns SQLITE_OK. -** -** If the journal file could not be written because the disk is full, -** then this routine returns SQLITE_FULL and does an immediate rollback. -** All subsequent write attempts also return SQLITE_FULL until there -** is a call to sqlite3pager_commit() or sqlite3pager_rollback() to -** reset. -*/ -int sqlite3pager_write(void *pData){ - PgHdr *pPg = DATA_TO_PGHDR(pData); - Pager *pPager = pPg->pPager; - int rc = SQLITE_OK; - - /* Check for errors - */ - if( pPager->errMask ){ - return pager_errcode(pPager); - } - if( pPager->readOnly ){ - return SQLITE_PERM; - } - - assert( !pPager->setMaster ); - - /* Mark the page as dirty. If the page has already been written - ** to the journal then we can return right away. - */ - pPg->dirty = 1; - if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){ - pPager->dirtyCache = 1; - return SQLITE_OK; - } - - /* If we get this far, it means that the page needs to be - ** written to the transaction journal or the ckeckpoint journal - ** or both. - ** - ** First check to see that the transaction journal exists and - ** create it if it does not. - */ - assert( pPager->state!=PAGER_UNLOCK ); - rc = sqlite3pager_begin(pData, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pPager->state>=PAGER_RESERVED ); - if( !pPager->journalOpen && pPager->useJournal ){ - rc = pager_open_journal(pPager); - if( rc!=SQLITE_OK ) return rc; - } - assert( pPager->journalOpen || !pPager->useJournal ); - pPager->dirtyCache = 1; - - /* The transaction journal now exists and we have a RESERVED or an - ** EXCLUSIVE lock on the main database file. Write the current page to - ** the transaction journal if it is not there already. - */ - if( !pPg->inJournal && (pPager->useJournal || pPager->memDb) ){ - if( (int)pPg->pgno <= pPager->origDbSize ){ - int szPg; - u32 saved; - if( pPager->memDb ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - TRACE3("JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - assert( pHist->pOrig==0 ); - pHist->pOrig = sqliteMallocRaw( pPager->pageSize ); - if( pHist->pOrig ){ - memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize); - } - }else{ - u32 cksum; - CODEC(pPager, pData, pPg->pgno, 7); - cksum = pager_cksum(pPager, pPg->pgno, pData); - saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager); - store32bits(cksum, pPg, pPager->pageSize); - szPg = pPager->pageSize+8; - store32bits(pPg->pgno, pPg, -4); - rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); - pPager->journalOff += szPg; - TRACE4("JOURNAL %d page %d needSync=%d\n", - pPager->fd.h, pPg->pgno, pPg->needSync); - CODEC(pPager, pData, pPg->pgno, 0); - *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved; - if( rc!=SQLITE_OK ){ - sqlite3pager_rollback(pPager); - pPager->errMask |= PAGER_ERR_FULL; - return rc; - } - pPager->nRec++; - assert( pPager->aInJournal!=0 ); - pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->needSync = !pPager->noSync; - if( pPager->stmtInUse ){ - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_stmt_list(pPg); - } - } - }else{ - pPg->needSync = !pPager->journalStarted && !pPager->noSync; - TRACE4("APPEND %d page %d needSync=%d\n", - pPager->fd.h, pPg->pgno, pPg->needSync); - } - if( pPg->needSync ){ - pPager->needSync = 1; - } - pPg->inJournal = 1; - } - - /* If the statement journal is open and the page is not in it, - ** then write the current page to the statement journal. Note that - ** the statement journal format differs from the standard journal format - ** in that it omits the checksums and the header. - */ - if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){ - assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); - if( pPager->memDb ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - assert( pHist->pStmt==0 ); - pHist->pStmt = sqliteMallocRaw( pPager->pageSize ); - if( pHist->pStmt ){ - memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize); - } - TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - }else{ - store32bits(pPg->pgno, pPg, -4); - CODEC(pPager, pData, pPg->pgno, 7); - rc = sqlite3OsWrite(&pPager->stfd, ((char*)pData)-4, pPager->pageSize+4); - TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - CODEC(pPager, pData, pPg->pgno, 0); - if( rc!=SQLITE_OK ){ - sqlite3pager_rollback(pPager); - pPager->errMask |= PAGER_ERR_FULL; - return rc; - } - pPager->stmtNRec++; - assert( pPager->aInStmt!=0 ); - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - } - page_add_to_stmt_list(pPg); - } - - /* Update the database size and return. - */ - if( pPager->dbSize<(int)pPg->pgno ){ - pPager->dbSize = pPg->pgno; - if( !pPager->memDb && pPager->dbSize==PENDING_BYTE/pPager->pageSize ){ - pPager->dbSize++; - } - } - return rc; -} - -/* -** Return TRUE if the page given in the argument was previously passed -** to sqlite3pager_write(). In other words, return TRUE if it is ok -** to change the content of the page. -*/ -int sqlite3pager_iswriteable(void *pData){ - PgHdr *pPg = DATA_TO_PGHDR(pData); - return pPg->dirty; -} - -/* -** Replace the content of a single page with the information in the third -** argument. -*/ -int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void *pData){ - void *pPage; - int rc; - - rc = sqlite3pager_get(pPager, pgno, &pPage); - if( rc==SQLITE_OK ){ - rc = sqlite3pager_write(pPage); - if( rc==SQLITE_OK ){ - memcpy(pPage, pData, pPager->pageSize); - } - sqlite3pager_unref(pPage); - } - return rc; -} - -/* -** A call to this routine tells the pager that it is not necessary to -** write the information on page "pgno" back to the disk, even though -** that page might be marked as dirty. -** -** The overlying software layer calls this routine when all of the data -** on the given page is unused. The pager marks the page as clean so -** that it does not get written to disk. -** -** Tests show that this optimization, together with the -** sqlite3pager_dont_rollback() below, more than double the speed -** of large INSERT operations and quadruple the speed of large DELETEs. -** -** When this routine is called, set the alwaysRollback flag to true. -** Subsequent calls to sqlite3pager_dont_rollback() for the same page -** will thereafter be ignored. This is necessary to avoid a problem -** where a page with data is added to the freelist during one part of -** a transaction then removed from the freelist during a later part -** of the same transaction and reused for some other purpose. When it -** is first added to the freelist, this routine is called. When reused, -** the dont_rollback() routine is called. But because the page contains -** critical data, we still need to be sure it gets rolled back in spite -** of the dont_rollback() call. -*/ -void sqlite3pager_dont_write(Pager *pPager, Pgno pgno){ - PgHdr *pPg; - - if( pPager->memDb ) return; - - pPg = pager_lookup(pPager, pgno); - pPg->alwaysRollback = 1; - if( pPg && pPg->dirty ){ - if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){ - /* If this pages is the last page in the file and the file has grown - ** during the current transaction, then do NOT mark the page as clean. - ** When the database file grows, we must make sure that the last page - ** gets written at least once so that the disk file will be the correct - ** size. If you do not write this page and the size of the file - ** on the disk ends up being too small, that can lead to database - ** corruption during the next transaction. - */ - }else{ - TRACE3("DONT_WRITE page %d of %d\n", pgno, pPager->fd.h); - pPg->dirty = 0; - } - } -} - -/* -** A call to this routine tells the pager that if a rollback occurs, -** it is not necessary to restore the data on the given page. This -** means that the pager does not have to record the given page in the -** rollback journal. -*/ -void sqlite3pager_dont_rollback(void *pData){ - PgHdr *pPg = DATA_TO_PGHDR(pData); - Pager *pPager = pPg->pPager; - - if( pPager->state!=PAGER_EXCLUSIVE || pPager->journalOpen==0 ) return; - if( pPg->alwaysRollback || pPager->alwaysRollback || pPager->memDb ) return; - if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){ - assert( pPager->aInJournal!=0 ); - pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->inJournal = 1; - if( pPager->stmtInUse ){ - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_stmt_list(pPg); - } - TRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, pPager->fd.h); - } - if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){ - assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); - assert( pPager->aInStmt!=0 ); - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_stmt_list(pPg); - } -} - - -/* -** Clear a PgHistory block -*/ -static void clearHistory(PgHistory *pHist){ - sqliteFree(pHist->pOrig); - sqliteFree(pHist->pStmt); - pHist->pOrig = 0; - pHist->pStmt = 0; -} - -/* -** Commit all changes to the database and release the write lock. -** -** If the commit fails for any reason, a rollback attempt is made -** and an error code is returned. If the commit worked, SQLITE_OK -** is returned. -*/ -int sqlite3pager_commit(Pager *pPager){ - int rc; - PgHdr *pPg; - - if( pPager->errMask==PAGER_ERR_FULL ){ - rc = sqlite3pager_rollback(pPager); - if( rc==SQLITE_OK ){ - rc = SQLITE_FULL; - } - return rc; - } - if( pPager->errMask!=0 ){ - rc = pager_errcode(pPager); - return rc; - } - if( pPager->state<PAGER_RESERVED ){ - return SQLITE_ERROR; - } - TRACE2("COMMIT %d\n", pPager->fd.h); - if( pPager->memDb ){ - pPg = pager_get_all_dirty_pages(pPager); - while( pPg ){ - clearHistory(PGHDR_TO_HIST(pPg, pPager)); - pPg->dirty = 0; - pPg->inJournal = 0; - pPg->inStmt = 0; - pPg->pPrevStmt = pPg->pNextStmt = 0; - pPg = pPg->pDirty; - } -#ifndef NDEBUG - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - assert( !pPg->alwaysRollback ); - assert( !pHist->pOrig ); - assert( !pHist->pStmt ); - } -#endif - pPager->pStmt = 0; - pPager->state = PAGER_SHARED; - return SQLITE_OK; - } - if( pPager->dirtyCache==0 ){ - /* Exit early (without doing the time-consuming sqlite3OsSync() calls) - ** if there have been no changes to the database file. */ - assert( pPager->needSync==0 ); - rc = pager_unwritelock(pPager); - pPager->dbSize = -1; - return rc; - } - assert( pPager->journalOpen ); - rc = sqlite3pager_sync(pPager, 0); - if( rc!=SQLITE_OK ){ - goto commit_abort; - } - rc = pager_unwritelock(pPager); - pPager->dbSize = -1; - return rc; - - /* Jump here if anything goes wrong during the commit process. - */ -commit_abort: - sqlite3pager_rollback(pPager); - return rc; -} - -/* -** Rollback all changes. The database falls back to PAGER_SHARED mode. -** All in-memory cache pages revert to their original data contents. -** The journal is deleted. -** -** This routine cannot fail unless some other process is not following -** the correct locking protocol (SQLITE_PROTOCOL) or unless some other -** process is writing trash into the journal file (SQLITE_CORRUPT) or -** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error -** codes are returned for all these occasions. Otherwise, -** SQLITE_OK is returned. -*/ -int sqlite3pager_rollback(Pager *pPager){ - int rc; - TRACE2("ROLLBACK %d\n", pPager->fd.h); - if( pPager->memDb ){ - PgHdr *p; - for(p=pPager->pAll; p; p=p->pNextAll){ - PgHistory *pHist; - assert( !p->alwaysRollback ); - if( !p->dirty ){ - assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig ); - assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt ); - continue; - } - - pHist = PGHDR_TO_HIST(p, pPager); - if( pHist->pOrig ){ - memcpy(PGHDR_TO_DATA(p), pHist->pOrig, pPager->pageSize); - TRACE3("ROLLBACK-PAGE %d of %d\n", p->pgno, pPager->fd.h); - }else{ - TRACE3("PAGE %d is clean on %d\n", p->pgno, pPager->fd.h); - } - clearHistory(pHist); - p->dirty = 0; - p->inJournal = 0; - p->inStmt = 0; - p->pPrevStmt = p->pNextStmt = 0; - - if( pPager->xReiniter ){ - pPager->xReiniter(PGHDR_TO_DATA(p), pPager->pageSize); - } - - } - pPager->pStmt = 0; - pPager->dbSize = pPager->origDbSize; - memoryTruncate(pPager); - pPager->stmtInUse = 0; - pPager->state = PAGER_SHARED; - return SQLITE_OK; - } - - if( !pPager->dirtyCache || !pPager->journalOpen ){ - rc = pager_unwritelock(pPager); - pPager->dbSize = -1; - return rc; - } - - if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){ - if( pPager->state>=PAGER_EXCLUSIVE ){ - pager_playback(pPager); - } - return pager_errcode(pPager); - } - if( pPager->state==PAGER_RESERVED ){ - int rc2, rc3; - rc = pager_reload_cache(pPager); - rc2 = pager_truncate(pPager, pPager->origDbSize); - rc3 = pager_unwritelock(pPager); - if( rc==SQLITE_OK ){ - rc = rc2; - if( rc3 ) rc = rc3; - } - }else{ - rc = pager_playback(pPager); - } - if( rc!=SQLITE_OK ){ - rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */ - pPager->errMask |= PAGER_ERR_CORRUPT; - } - pPager->dbSize = -1; - return rc; -} - -/* -** Return TRUE if the database file is opened read-only. Return FALSE -** if the database is (in theory) writable. -*/ -int sqlite3pager_isreadonly(Pager *pPager){ - return pPager->readOnly; -} - -/* -** This routine is used for testing and analysis only. -*/ -int *sqlite3pager_stats(Pager *pPager){ - static int a[9]; - a[0] = pPager->nRef; - a[1] = pPager->nPage; - a[2] = pPager->mxPage; - a[3] = pPager->dbSize; - a[4] = pPager->state; - a[5] = pPager->errMask; - a[6] = pPager->nHit; - a[7] = pPager->nMiss; - a[8] = pPager->nOvfl; - return a; -} - -/* -** Set the statement rollback point. -** -** This routine should be called with the transaction journal already -** open. A new statement journal is created that can be used to rollback -** changes of a single SQL command within a larger transaction. -*/ -int sqlite3pager_stmt_begin(Pager *pPager){ - int rc; - char zTemp[SQLITE_TEMPNAME_SIZE]; - assert( !pPager->stmtInUse ); - assert( pPager->dbSize>=0 ); - TRACE2("STMT-BEGIN %d\n", pPager->fd.h); - if( pPager->memDb ){ - pPager->stmtInUse = 1; - pPager->stmtSize = pPager->dbSize; - return SQLITE_OK; - } - if( !pPager->journalOpen ){ - pPager->stmtAutoopen = 1; - return SQLITE_OK; - } - assert( pPager->journalOpen ); - pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 ); - if( pPager->aInStmt==0 ){ - sqlite3OsLock(&pPager->fd, SHARED_LOCK); - return SQLITE_NOMEM; - } -#ifndef NDEBUG - rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize); - if( rc ) goto stmt_begin_failed; - assert( pPager->stmtJSize == pPager->journalOff ); -#endif - pPager->stmtJSize = pPager->journalOff; - pPager->stmtSize = pPager->dbSize; - pPager->stmtHdrOff = 0; - pPager->stmtCksum = pPager->cksumInit; - if( !pPager->stmtOpen ){ - rc = sqlite3pager_opentemp(zTemp, &pPager->stfd); - if( rc ) goto stmt_begin_failed; - pPager->stmtOpen = 1; - pPager->stmtNRec = 0; - } - pPager->stmtInUse = 1; - return SQLITE_OK; - -stmt_begin_failed: - if( pPager->aInStmt ){ - sqliteFree(pPager->aInStmt); - pPager->aInStmt = 0; - } - return rc; -} - -/* -** Commit a statement. -*/ -int sqlite3pager_stmt_commit(Pager *pPager){ - if( pPager->stmtInUse ){ - PgHdr *pPg, *pNext; - TRACE2("STMT-COMMIT %d\n", pPager->fd.h); - if( !pPager->memDb ){ - sqlite3OsSeek(&pPager->stfd, 0); - /* sqlite3OsTruncate(&pPager->stfd, 0); */ - sqliteFree( pPager->aInStmt ); - pPager->aInStmt = 0; - } - for(pPg=pPager->pStmt; pPg; pPg=pNext){ - pNext = pPg->pNextStmt; - assert( pPg->inStmt ); - pPg->inStmt = 0; - pPg->pPrevStmt = pPg->pNextStmt = 0; - if( pPager->memDb ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - sqliteFree(pHist->pStmt); - pHist->pStmt = 0; - } - } - pPager->stmtNRec = 0; - pPager->stmtInUse = 0; - pPager->pStmt = 0; - } - pPager->stmtAutoopen = 0; - return SQLITE_OK; -} - -/* -** Rollback a statement. -*/ -int sqlite3pager_stmt_rollback(Pager *pPager){ - int rc; - if( pPager->stmtInUse ){ - TRACE2("STMT-ROLLBACK %d\n", pPager->fd.h); - if( pPager->memDb ){ - PgHdr *pPg; - for(pPg=pPager->pStmt; pPg; pPg=pPg->pNextStmt){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - if( pHist->pStmt ){ - memcpy(PGHDR_TO_DATA(pPg), pHist->pStmt, pPager->pageSize); - sqliteFree(pHist->pStmt); - pHist->pStmt = 0; - } - } - pPager->dbSize = pPager->stmtSize; - memoryTruncate(pPager); - rc = SQLITE_OK; - }else{ - rc = pager_stmt_playback(pPager); - } - sqlite3pager_stmt_commit(pPager); - }else{ - rc = SQLITE_OK; - } - pPager->stmtAutoopen = 0; - return rc; -} - -/* -** Return the full pathname of the database file. -*/ -const char *sqlite3pager_filename(Pager *pPager){ - return pPager->zFilename; -} - -/* -** Return the directory of the database file. -*/ -const char *sqlite3pager_dirname(Pager *pPager){ - return pPager->zDirectory; -} - -/* -** Return the full pathname of the journal file. -*/ -const char *sqlite3pager_journalname(Pager *pPager){ - return pPager->zJournal; -} - -/* -** Set the codec for this pager -*/ -void sqlite3pager_set_codec( - Pager *pPager, - void (*xCodec)(void*,void*,Pgno,int), - void *pCodecArg -){ - pPager->xCodec = xCodec; - pPager->pCodecArg = pCodecArg; -} - -/* -** This routine is called to increment the database file change-counter, -** stored at byte 24 of the pager file. -*/ -static int pager_incr_changecounter(Pager *pPager){ - void *pPage; - PgHdr *pPgHdr; - u32 change_counter; - int rc; - - /* Open page 1 of the file for writing. */ - rc = sqlite3pager_get(pPager, 1, &pPage); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3pager_write(pPage); - if( rc!=SQLITE_OK ) return rc; - - /* Read the current value at byte 24. */ - pPgHdr = DATA_TO_PGHDR(pPage); - change_counter = retrieve32bits(pPgHdr, 24); - - /* Increment the value just read and write it back to byte 24. */ - change_counter++; - store32bits(change_counter, pPgHdr, 24); - - /* Release the page reference. */ - sqlite3pager_unref(pPage); - return SQLITE_OK; -} - -/* -** Sync the database file for the pager pPager. zMaster points to the name -** of a master journal file that should be written into the individual -** journal file. zMaster may be NULL, which is interpreted as no master -** journal (a single database transaction). -** -** This routine ensures that the journal is synced, all dirty pages written -** to the database file and the database file synced. The only thing that -** remains to commit the transaction is to delete the journal file (or -** master journal file if specified). -** -** Note that if zMaster==NULL, this does not overwrite a previous value -** passed to an sqlite3pager_sync() call. -*/ -int sqlite3pager_sync(Pager *pPager, const char *zMaster){ - int rc = SQLITE_OK; - - /* If this is an in-memory db, or no pages have been written to, or this - ** function has already been called, it is a no-op. - */ - if( pPager->state!=PAGER_SYNCED && !pPager->memDb && pPager->dirtyCache ){ - PgHdr *pPg; - assert( pPager->journalOpen ); - - /* If a master journal file name has already been written to the - ** journal file, then no sync is required. This happens when it is - ** written, then the process fails to upgrade from a RESERVED to an - ** EXCLUSIVE lock. The next time the process tries to commit the - ** transaction the m-j name will have already been written. - */ - if( !pPager->setMaster ){ - rc = pager_incr_changecounter(pPager); - if( rc!=SQLITE_OK ) goto sync_exit; - rc = writeMasterJournal(pPager, zMaster); - if( rc!=SQLITE_OK ) goto sync_exit; - rc = syncJournal(pPager); - if( rc!=SQLITE_OK ) goto sync_exit; - } - - /* Write all dirty pages to the database file */ - pPg = pager_get_all_dirty_pages(pPager); - rc = pager_write_pagelist(pPg); - if( rc!=SQLITE_OK ) goto sync_exit; - - /* Sync the database file. */ - if( !pPager->noSync ){ - rc = sqlite3OsSync(&pPager->fd); - } - - pPager->state = PAGER_SYNCED; - } - -sync_exit: - return rc; -} - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -/* -** Return the current state of the file lock for the given pager. -** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK, -** PENDING_LOCK, or EXCLUSIVE_LOCK. -*/ -int sqlite3pager_lockstate(Pager *pPager){ -#ifdef OS_TEST - return pPager->fd->fd.locktype; -#else - return pPager->fd.locktype; -#endif -} -#endif - -#ifdef SQLITE_TEST -/* -** Print a listing of all referenced pages and their ref count. -*/ -void sqlite3pager_refdump(Pager *pPager){ - PgHdr *pPg; - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ - if( pPg->nRef<=0 ) continue; - sqlite3DebugPrintf("PAGE %3d addr=%p nRef=%d\n", - pPg->pgno, PGHDR_TO_DATA(pPg), pPg->nRef); - } -} -#endif diff --git a/kopete/plugins/statistics/sqlite/pager.h b/kopete/plugins/statistics/sqlite/pager.h deleted file mode 100644 index 0231e27a..00000000 --- a/kopete/plugins/statistics/sqlite/pager.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the sqlite page cache -** subsystem. The page cache subsystem reads and writes a file a page -** at a time and provides a journal for rollback. -** -** @(#) $Id$ -*/ - -/* -** The default size of a database page. -*/ -#ifndef SQLITE_DEFAULT_PAGE_SIZE -# define SQLITE_DEFAULT_PAGE_SIZE 1024 -#endif - -/* Maximum page size. The upper bound on this value is 65536 (a limit -** imposed by the 2-byte size of cell array pointers.) The -** maximum page size determines the amount of stack space allocated -** by many of the routines in pager.c and btree.c On embedded architectures -** or any machine where memory and especially stack memory is limited, -** one may wish to chose a smaller value for the maximum page size. -*/ -#ifndef SQLITE_MAX_PAGE_SIZE -# define SQLITE_MAX_PAGE_SIZE 8192 -#endif - -/* -** Maximum number of pages in one database. -*/ -#define SQLITE_MAX_PAGE 1073741823 - -/* -** The type used to represent a page number. The first page in a file -** is called page 1. 0 is used to represent "not a page". -*/ -typedef unsigned int Pgno; - -/* -** Each open file is managed by a separate instance of the "Pager" structure. -*/ -typedef struct Pager Pager; - - -/* -** See source code comments for a detailed description of the following -** routines: -*/ -int sqlite3pager_open(Pager **ppPager, const char *zFilename, - int nExtra, int useJournal); -void sqlite3pager_set_busyhandler(Pager*, BusyHandler *pBusyHandler); -void sqlite3pager_set_destructor(Pager*, void(*)(void*,int)); -void sqlite3pager_set_reiniter(Pager*, void(*)(void*,int)); -void sqlite3pager_set_pagesize(Pager*, int); -void sqlite3pager_read_fileheader(Pager*, int, unsigned char*); -void sqlite3pager_set_cachesize(Pager*, int); -int sqlite3pager_close(Pager *pPager); -int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage); -void *sqlite3pager_lookup(Pager *pPager, Pgno pgno); -int sqlite3pager_ref(void*); -int sqlite3pager_unref(void*); -Pgno sqlite3pager_pagenumber(void*); -int sqlite3pager_write(void*); -int sqlite3pager_iswriteable(void*); -int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*); -int sqlite3pager_pagecount(Pager*); -int sqlite3pager_truncate(Pager*,Pgno); -int sqlite3pager_begin(void*, int exFlag); -int sqlite3pager_commit(Pager*); -int sqlite3pager_sync(Pager*,const char *zMaster); -int sqlite3pager_rollback(Pager*); -int sqlite3pager_isreadonly(Pager*); -int sqlite3pager_stmt_begin(Pager*); -int sqlite3pager_stmt_commit(Pager*); -int sqlite3pager_stmt_rollback(Pager*); -void sqlite3pager_dont_rollback(void*); -void sqlite3pager_dont_write(Pager*, Pgno); -int *sqlite3pager_stats(Pager*); -void sqlite3pager_set_safety_level(Pager*,int); -const char *sqlite3pager_filename(Pager*); -const char *sqlite3pager_dirname(Pager*); -const char *sqlite3pager_journalname(Pager*); -int sqlite3pager_rename(Pager*, const char *zNewName); -void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*); - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -int sqlite3pager_lockstate(Pager*); -#endif - -#ifdef SQLITE_TEST -void sqlite3pager_refdump(Pager*); -int pager3_refinfo_enable; -#endif diff --git a/kopete/plugins/statistics/sqlite/parse.c b/kopete/plugins/statistics/sqlite/parse.c deleted file mode 100644 index d3e68e02..00000000 --- a/kopete/plugins/statistics/sqlite/parse.c +++ /dev/null @@ -1,3143 +0,0 @@ -/* Driver template for the LEMON parser generator. -** The author disclaims copyright to this source code. -*/ -/* First off, code is include which follows the "include" declaration -** in the input file. */ -#include <stdio.h> -#line 33 "parse.y" - -#include "sqliteInt.h" -#include "parse.h" - -/* -** An instance of this structure holds information about the -** LIMIT clause of a SELECT statement. -*/ -struct LimitVal { - int limit; /* The LIMIT value. -1 if there is no limit */ - int offset; /* The OFFSET. 0 if there is none */ -}; - -/* -** An instance of this structure is used to store the LIKE, -** GLOB, NOT LIKE, and NOT GLOB operators. -*/ -struct LikeOp { - int opcode; /* Either TK_GLOB or TK_LIKE */ - int not; /* True if the NOT keyword is present */ -}; - -/* -** An instance of the following structure describes the event of a -** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT, -** TK_DELETE, or TK_INSTEAD. If the event is of the form -** -** UPDATE ON (a,b,c) -** -** Then the "b" IdList records the list "a,b,c". -*/ -struct TrigEvent { int a; IdList * b; }; - -/* -** An instance of this structure holds the ATTACH key and the key type. -*/ -struct AttachKey { int type; Token key; }; - -#line 48 "parse.c" -/* Next is all token values, in a form suitable for use by makeheaders. -** This section will be null unless lemon is run with the -m switch. -*/ -/* -** These constants (all generated automatically by the parser generator) -** specify the various kinds of tokens (terminals) that the parser -** understands. -** -** Each symbol here is a terminal symbol in the grammar. -*/ -/* Make sure the INTERFACE macro is defined. -*/ -#ifndef INTERFACE -# define INTERFACE 1 -#endif -/* The next thing included is series of defines which control -** various aspects of the generated parser. -** YYCODETYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 terminals -** and nonterminals. "int" is used otherwise. -** YYNOCODE is a number of type YYCODETYPE which corresponds -** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash -** table. -** YYFALLBACK If defined, this indicates that one or more tokens -** have fall-back values which should be used if the -** original value of the token will not parse. -** YYACTIONTYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 rules and -** states combined. "int" is used otherwise. -** sqlite3ParserTOKENTYPE is the data type used for minor tokens given -** directly to the parser from the tokenizer. -** YYMINORTYPE is the data type used for all minor tokens. -** This is typically a union of many types, one of -** which is sqlite3ParserTOKENTYPE. The entry in the union -** for base tokens is called "yy0". -** YYSTACKDEPTH is the maximum depth of the parser's stack. -** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument -** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument -** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser -** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser -** YYNSTATE the combined number of states. -** YYNRULE the number of rules in the grammar -** YYERRORSYMBOL is the code number of the error symbol. If not -** defined, then do no error processing. -*/ -#define YYCODETYPE unsigned char -#define YYNOCODE 225 -#define YYACTIONTYPE unsigned short int -#define sqlite3ParserTOKENTYPE Token -typedef union { - sqlite3ParserTOKENTYPE yy0; - struct {int value; int mask;} yy47; - TriggerStep* yy91; - Token yy98; - Select* yy107; - struct TrigEvent yy146; - ExprList* yy210; - Expr* yy258; - SrcList* yy259; - IdList* yy272; - int yy284; - struct AttachKey yy292; - struct LikeOp yy342; - struct LimitVal yy404; - int yy449; -} YYMINORTYPE; -#define YYSTACKDEPTH 100 -#define sqlite3ParserARG_SDECL Parse *pParse; -#define sqlite3ParserARG_PDECL ,Parse *pParse -#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse -#define sqlite3ParserARG_STORE yypParser->pParse = pParse -#define YYNSTATE 537 -#define YYNRULE 292 -#define YYERRORSYMBOL 130 -#define YYERRSYMDT yy449 -#define YYFALLBACK 1 -#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) -#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) -#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) - -/* Next are that tables used to determine what action to take based on the -** current state and lookahead token. These tables are used to implement -** functions that take a state number and lookahead value and return an -** action integer. -** -** Suppose the action integer is N. Then the action is determined as -** follows -** -** 0 <= N < YYNSTATE Shift N. That is, push the lookahead -** token onto the stack and goto state N. -** -** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. -** -** N == YYNSTATE+YYNRULE A syntax error has occurred. -** -** N == YYNSTATE+YYNRULE+1 The parser accepts its input. -** -** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused -** slots in the yy_action[] table. -** -** The action table is constructed as a single large table named yy_action[]. -** Given state S and lookahead X, the action is computed as -** -** yy_action[ yy_shift_ofst[S] + X ] -** -** If the index value yy_shift_ofst[S]+X is out of range or if the value -** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] -** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. -** -** The formula above is for computing the action when the lookahead is -** a terminal symbol. If the lookahead is a non-terminal (as occurs after -** a reduce action) then the yy_reduce_ofst[] array is used in place of -** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of -** YY_SHIFT_USE_DFLT. -** -** The following are the tables generated in this section: -** -** yy_action[] A single table containing all actions. -** yy_lookahead[] A table containing the lookahead for each entry in -** yy_action. Used to detect hash collisions. -** yy_shift_ofst[] For each state, the offset into yy_action for -** shifting terminals. -** yy_reduce_ofst[] For each state, the offset into yy_action for -** shifting non-terminals after a reduce. -** yy_default[] Default action for each state. -*/ -static const YYACTIONTYPE yy_action[] = { - /* 0 */ 257, 325, 255, 138, 140, 142, 144, 146, 148, 150, - /* 10 */ 152, 154, 156, 89, 87, 88, 159, 12, 4, 6, - /* 20 */ 158, 537, 38, 24, 830, 1, 536, 3, 329, 488, - /* 30 */ 534, 535, 319, 50, 124, 112, 160, 169, 174, 179, - /* 40 */ 168, 173, 134, 136, 128, 130, 126, 132, 138, 140, - /* 50 */ 142, 144, 146, 148, 150, 152, 154, 156, 26, 73, - /* 60 */ 384, 256, 39, 58, 64, 66, 299, 330, 612, 611, - /* 70 */ 351, 30, 92, 332, 326, 159, 13, 14, 353, 158, - /* 80 */ 5, 355, 361, 366, 499, 146, 148, 150, 152, 154, - /* 90 */ 156, 12, 369, 124, 112, 160, 169, 174, 179, 168, - /* 100 */ 173, 134, 136, 128, 130, 126, 132, 138, 140, 142, - /* 110 */ 144, 146, 148, 150, 152, 154, 156, 128, 130, 126, - /* 120 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154, - /* 130 */ 156, 659, 353, 244, 62, 355, 361, 366, 79, 12, - /* 140 */ 63, 98, 96, 289, 159, 280, 369, 349, 158, 181, - /* 150 */ 13, 14, 27, 12, 546, 383, 32, 10, 368, 273, - /* 160 */ 515, 765, 124, 112, 160, 169, 174, 179, 168, 173, - /* 170 */ 134, 136, 128, 130, 126, 132, 138, 140, 142, 144, - /* 180 */ 146, 148, 150, 152, 154, 156, 810, 349, 47, 73, - /* 190 */ 222, 763, 223, 114, 246, 31, 32, 48, 13, 14, - /* 200 */ 74, 274, 252, 166, 175, 180, 275, 304, 49, 8, - /* 210 */ 255, 45, 13, 14, 159, 290, 350, 382, 158, 245, - /* 220 */ 441, 46, 378, 183, 247, 185, 186, 15, 16, 17, - /* 230 */ 73, 205, 124, 112, 160, 169, 174, 179, 168, 173, - /* 240 */ 134, 136, 128, 130, 126, 132, 138, 140, 142, 144, - /* 250 */ 146, 148, 150, 152, 154, 156, 542, 306, 438, 159, - /* 260 */ 98, 96, 332, 158, 272, 475, 447, 437, 12, 256, - /* 270 */ 288, 12, 304, 339, 287, 50, 77, 124, 112, 160, - /* 280 */ 169, 174, 179, 168, 173, 134, 136, 128, 130, 126, - /* 290 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154, - /* 300 */ 156, 547, 36, 335, 39, 58, 64, 66, 299, 330, - /* 310 */ 35, 334, 291, 545, 114, 332, 114, 329, 12, 625, - /* 320 */ 353, 187, 306, 355, 361, 366, 422, 13, 14, 159, - /* 330 */ 13, 14, 184, 158, 369, 636, 188, 259, 188, 764, - /* 340 */ 91, 87, 88, 100, 87, 88, 219, 124, 112, 160, - /* 350 */ 169, 174, 179, 168, 173, 134, 136, 128, 130, 126, - /* 360 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154, - /* 370 */ 156, 297, 282, 114, 292, 51, 237, 13, 14, 150, - /* 380 */ 152, 154, 156, 114, 12, 225, 53, 225, 159, 166, - /* 390 */ 175, 180, 158, 380, 303, 111, 433, 658, 69, 92, - /* 400 */ 379, 183, 92, 185, 186, 111, 124, 112, 160, 169, - /* 410 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132, - /* 420 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, - /* 430 */ 103, 230, 561, 159, 773, 12, 286, 158, 631, 534, - /* 440 */ 535, 105, 815, 13, 14, 166, 175, 180, 203, 808, - /* 450 */ 215, 124, 112, 160, 169, 174, 179, 168, 173, 134, - /* 460 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146, - /* 470 */ 148, 150, 152, 154, 156, 2, 3, 183, 159, 185, - /* 480 */ 186, 813, 158, 43, 44, 569, 33, 633, 41, 348, - /* 490 */ 340, 413, 415, 414, 13, 14, 124, 112, 160, 169, - /* 500 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132, - /* 510 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, - /* 520 */ 249, 336, 697, 159, 337, 338, 183, 158, 185, 186, - /* 530 */ 56, 57, 183, 11, 185, 186, 183, 416, 185, 186, - /* 540 */ 402, 124, 112, 160, 169, 174, 179, 168, 173, 134, - /* 550 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146, - /* 560 */ 148, 150, 152, 154, 156, 342, 87, 88, 159, 345, - /* 570 */ 87, 88, 158, 98, 96, 183, 404, 185, 186, 240, - /* 580 */ 9, 183, 92, 185, 186, 802, 124, 177, 160, 169, - /* 590 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132, - /* 600 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, - /* 610 */ 787, 341, 257, 159, 255, 255, 183, 158, 185, 186, - /* 620 */ 94, 95, 480, 518, 92, 307, 314, 316, 92, 548, - /* 630 */ 325, 171, 112, 160, 169, 174, 179, 168, 173, 134, - /* 640 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146, - /* 650 */ 148, 150, 152, 154, 156, 255, 25, 486, 159, 482, - /* 660 */ 170, 358, 158, 19, 241, 242, 252, 266, 513, 267, - /* 670 */ 259, 553, 72, 256, 256, 402, 68, 244, 160, 169, - /* 680 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132, - /* 690 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, - /* 700 */ 207, 255, 72, 326, 780, 260, 68, 267, 514, 47, - /* 710 */ 189, 428, 388, 385, 256, 325, 259, 21, 48, 162, - /* 720 */ 395, 12, 114, 161, 516, 517, 195, 193, 294, 49, - /* 730 */ 207, 484, 209, 312, 191, 70, 71, 387, 246, 113, - /* 740 */ 189, 164, 165, 73, 198, 114, 363, 396, 114, 391, - /* 750 */ 73, 277, 529, 313, 436, 182, 195, 193, 72, 467, - /* 760 */ 256, 623, 68, 245, 191, 70, 71, 188, 163, 113, - /* 770 */ 188, 119, 120, 121, 122, 197, 114, 803, 691, 72, - /* 780 */ 13, 14, 92, 68, 73, 73, 207, 77, 326, 73, - /* 790 */ 199, 807, 99, 436, 452, 293, 189, 223, 474, 325, - /* 800 */ 309, 119, 120, 121, 122, 197, 423, 207, 221, 460, - /* 810 */ 434, 419, 195, 193, 418, 90, 224, 189, 77, 225, - /* 820 */ 191, 70, 71, 73, 442, 113, 420, 114, 325, 444, - /* 830 */ 372, 468, 114, 195, 193, 283, 325, 311, 310, 402, - /* 840 */ 470, 191, 70, 71, 114, 7, 113, 41, 460, 474, - /* 850 */ 18, 20, 22, 386, 296, 114, 457, 119, 120, 121, - /* 860 */ 122, 197, 766, 446, 521, 554, 123, 430, 444, 23, - /* 870 */ 531, 114, 326, 114, 114, 481, 114, 125, 119, 120, - /* 880 */ 121, 122, 197, 510, 72, 441, 114, 238, 68, 114, - /* 890 */ 508, 506, 114, 127, 114, 129, 131, 114, 133, 411, - /* 900 */ 412, 322, 114, 114, 114, 114, 407, 114, 135, 326, - /* 910 */ 660, 137, 207, 114, 139, 114, 141, 451, 114, 143, - /* 920 */ 114, 114, 189, 114, 145, 147, 149, 151, 114, 153, - /* 930 */ 489, 493, 437, 114, 114, 155, 479, 157, 195, 193, - /* 940 */ 167, 77, 176, 178, 114, 190, 191, 70, 71, 114, - /* 950 */ 192, 113, 114, 114, 114, 194, 196, 114, 691, 114, - /* 960 */ 269, 320, 343, 321, 344, 269, 204, 114, 359, 284, - /* 970 */ 321, 206, 114, 555, 216, 218, 220, 114, 364, 234, - /* 980 */ 321, 239, 660, 119, 120, 121, 122, 197, 373, 271, - /* 990 */ 321, 281, 114, 114, 367, 227, 227, 269, 431, 408, - /* 1000 */ 321, 503, 439, 44, 465, 473, 267, 471, 114, 77, - /* 1010 */ 402, 402, 402, 402, 455, 459, 265, 457, 402, 402, - /* 1020 */ 823, 417, 504, 507, 556, 471, 28, 29, 560, 37, - /* 1030 */ 472, 73, 34, 55, 40, 41, 42, 54, 59, 67, - /* 1040 */ 570, 571, 52, 75, 60, 78, 483, 485, 487, 491, - /* 1050 */ 61, 65, 76, 464, 495, 501, 101, 527, 77, 238, - /* 1060 */ 233, 235, 85, 93, 86, 80, 97, 238, 102, 81, - /* 1070 */ 104, 82, 108, 107, 109, 110, 83, 115, 497, 84, - /* 1080 */ 117, 116, 156, 172, 637, 217, 638, 118, 202, 226, - /* 1090 */ 639, 208, 106, 211, 227, 210, 213, 214, 212, 229, - /* 1100 */ 228, 231, 236, 223, 200, 243, 201, 251, 248, 250, - /* 1110 */ 254, 253, 232, 258, 261, 270, 264, 263, 262, 268, - /* 1120 */ 276, 278, 285, 295, 318, 279, 300, 303, 301, 305, - /* 1130 */ 333, 346, 298, 323, 327, 356, 357, 362, 370, 302, - /* 1140 */ 371, 53, 374, 394, 399, 354, 331, 375, 401, 409, - /* 1150 */ 308, 347, 315, 324, 406, 317, 405, 328, 795, 390, - /* 1160 */ 389, 392, 397, 410, 421, 800, 360, 381, 365, 393, - /* 1170 */ 398, 352, 376, 403, 801, 377, 400, 425, 426, 424, - /* 1180 */ 427, 429, 771, 432, 772, 435, 440, 698, 443, 794, - /* 1190 */ 445, 438, 809, 449, 699, 450, 453, 448, 454, 456, - /* 1200 */ 811, 458, 461, 462, 463, 469, 812, 814, 476, 630, - /* 1210 */ 478, 632, 779, 821, 490, 477, 690, 492, 494, 496, - /* 1220 */ 498, 693, 500, 505, 696, 509, 781, 511, 782, 783, - /* 1230 */ 466, 784, 785, 502, 512, 786, 520, 822, 519, 530, - /* 1240 */ 524, 824, 523, 825, 525, 528, 533, 828, 518, 518, - /* 1250 */ 518, 518, 518, 518, 522, 518, 526, 518, 518, 532, -}; -static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 24, 139, 26, 72, 73, 74, 75, 76, 77, 78, - /* 10 */ 79, 80, 81, 154, 155, 156, 40, 26, 135, 136, - /* 20 */ 44, 0, 158, 140, 131, 132, 133, 134, 164, 146, - /* 30 */ 9, 10, 170, 60, 58, 59, 60, 61, 62, 63, - /* 40 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - /* 50 */ 74, 75, 76, 77, 78, 79, 80, 81, 22, 176, - /* 60 */ 24, 85, 89, 90, 91, 92, 93, 94, 23, 23, - /* 70 */ 25, 25, 213, 100, 212, 40, 85, 86, 87, 44, - /* 80 */ 9, 90, 91, 92, 201, 76, 77, 78, 79, 80, - /* 90 */ 81, 26, 101, 58, 59, 60, 61, 62, 63, 64, - /* 100 */ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - /* 110 */ 75, 76, 77, 78, 79, 80, 81, 68, 69, 70, - /* 120 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 130 */ 81, 23, 87, 25, 29, 90, 91, 92, 179, 26, - /* 140 */ 35, 76, 77, 23, 40, 186, 101, 139, 44, 22, - /* 150 */ 85, 86, 144, 26, 9, 147, 148, 12, 159, 146, - /* 160 */ 95, 126, 58, 59, 60, 61, 62, 63, 64, 65, - /* 170 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - /* 180 */ 76, 77, 78, 79, 80, 81, 17, 139, 18, 176, - /* 190 */ 23, 17, 25, 139, 86, 147, 148, 27, 85, 86, - /* 200 */ 146, 188, 189, 204, 205, 206, 193, 45, 38, 137, - /* 210 */ 26, 41, 85, 86, 40, 161, 168, 169, 44, 111, - /* 220 */ 51, 51, 60, 103, 111, 105, 106, 13, 14, 15, - /* 230 */ 176, 127, 58, 59, 60, 61, 62, 63, 64, 65, - /* 240 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - /* 250 */ 76, 77, 78, 79, 80, 81, 9, 95, 58, 40, - /* 260 */ 76, 77, 100, 44, 22, 96, 97, 98, 26, 85, - /* 270 */ 104, 26, 45, 89, 108, 60, 107, 58, 59, 60, - /* 280 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, - /* 290 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 300 */ 81, 9, 87, 88, 89, 90, 91, 92, 93, 94, - /* 310 */ 157, 158, 23, 9, 139, 100, 139, 164, 26, 119, - /* 320 */ 87, 23, 95, 90, 91, 92, 21, 85, 86, 40, - /* 330 */ 85, 86, 104, 44, 101, 107, 161, 152, 161, 17, - /* 340 */ 154, 155, 156, 154, 155, 156, 127, 58, 59, 60, - /* 350 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, - /* 360 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 370 */ 81, 23, 187, 139, 199, 89, 199, 85, 86, 78, - /* 380 */ 79, 80, 81, 139, 26, 210, 100, 210, 40, 204, - /* 390 */ 205, 206, 44, 164, 165, 161, 91, 23, 22, 213, - /* 400 */ 171, 103, 213, 105, 106, 161, 58, 59, 60, 61, - /* 410 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - /* 420 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - /* 430 */ 196, 197, 9, 40, 129, 26, 78, 44, 9, 9, - /* 440 */ 10, 197, 9, 85, 86, 204, 205, 206, 126, 11, - /* 450 */ 128, 58, 59, 60, 61, 62, 63, 64, 65, 66, - /* 460 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 470 */ 77, 78, 79, 80, 81, 133, 134, 103, 40, 105, - /* 480 */ 106, 9, 44, 173, 174, 109, 149, 9, 95, 152, - /* 490 */ 153, 96, 97, 98, 85, 86, 58, 59, 60, 61, - /* 500 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - /* 510 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - /* 520 */ 111, 152, 9, 40, 155, 156, 103, 44, 105, 106, - /* 530 */ 13, 14, 103, 139, 105, 106, 103, 47, 105, 106, - /* 540 */ 139, 58, 59, 60, 61, 62, 63, 64, 65, 66, - /* 550 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 560 */ 77, 78, 79, 80, 81, 154, 155, 156, 40, 154, - /* 570 */ 155, 156, 44, 76, 77, 103, 175, 105, 106, 25, - /* 580 */ 138, 103, 213, 105, 106, 95, 58, 59, 60, 61, - /* 590 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - /* 600 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - /* 610 */ 9, 22, 24, 40, 26, 26, 103, 44, 105, 106, - /* 620 */ 121, 122, 20, 22, 213, 96, 97, 98, 213, 9, - /* 630 */ 139, 60, 59, 60, 61, 62, 63, 64, 65, 66, - /* 640 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 650 */ 77, 78, 79, 80, 81, 26, 141, 55, 40, 57, - /* 660 */ 89, 170, 44, 138, 110, 188, 189, 23, 67, 25, - /* 670 */ 152, 9, 22, 85, 85, 139, 26, 25, 60, 61, - /* 680 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - /* 690 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - /* 700 */ 50, 26, 22, 212, 9, 187, 26, 25, 139, 18, - /* 710 */ 60, 175, 20, 146, 85, 139, 152, 138, 27, 40, - /* 720 */ 146, 26, 139, 44, 155, 156, 76, 77, 78, 38, - /* 730 */ 50, 129, 41, 32, 84, 85, 86, 142, 86, 89, - /* 740 */ 60, 62, 63, 176, 161, 139, 170, 55, 139, 57, - /* 750 */ 176, 187, 123, 52, 146, 146, 76, 77, 22, 146, - /* 760 */ 85, 9, 26, 111, 84, 85, 86, 161, 89, 89, - /* 770 */ 161, 121, 122, 123, 124, 125, 139, 95, 9, 22, - /* 780 */ 85, 86, 213, 26, 176, 176, 50, 107, 212, 176, - /* 790 */ 207, 11, 25, 146, 25, 23, 60, 25, 161, 139, - /* 800 */ 99, 121, 122, 123, 124, 125, 211, 50, 199, 201, - /* 810 */ 215, 28, 76, 77, 31, 48, 210, 60, 107, 210, - /* 820 */ 84, 85, 86, 176, 216, 89, 43, 139, 139, 221, - /* 830 */ 170, 120, 139, 76, 77, 78, 139, 88, 89, 139, - /* 840 */ 203, 84, 85, 86, 139, 11, 89, 95, 201, 161, - /* 850 */ 16, 17, 18, 19, 161, 139, 139, 121, 122, 123, - /* 860 */ 124, 125, 126, 216, 30, 9, 161, 170, 221, 138, - /* 870 */ 36, 139, 212, 139, 139, 175, 139, 161, 121, 122, - /* 880 */ 123, 124, 125, 49, 22, 51, 139, 118, 26, 139, - /* 890 */ 56, 203, 139, 161, 139, 161, 161, 139, 161, 53, - /* 900 */ 54, 212, 139, 139, 139, 139, 126, 139, 161, 212, - /* 910 */ 24, 161, 50, 139, 161, 139, 161, 200, 139, 161, - /* 920 */ 139, 139, 60, 139, 161, 161, 161, 161, 139, 161, - /* 930 */ 96, 97, 98, 139, 139, 161, 102, 161, 76, 77, - /* 940 */ 161, 107, 161, 161, 139, 161, 84, 85, 86, 139, - /* 950 */ 161, 89, 139, 139, 139, 161, 161, 139, 9, 139, - /* 960 */ 139, 23, 23, 25, 25, 139, 161, 139, 23, 139, - /* 970 */ 25, 161, 139, 9, 161, 161, 161, 139, 23, 161, - /* 980 */ 25, 161, 95, 121, 122, 123, 124, 125, 23, 161, - /* 990 */ 25, 161, 139, 139, 161, 109, 109, 139, 23, 161, - /* 1000 */ 25, 146, 173, 174, 23, 23, 25, 25, 139, 107, - /* 1010 */ 139, 139, 139, 139, 161, 161, 195, 139, 139, 139, - /* 1020 */ 9, 195, 120, 23, 9, 25, 145, 23, 9, 139, - /* 1030 */ 161, 176, 150, 42, 159, 95, 33, 167, 46, 22, - /* 1040 */ 109, 109, 159, 177, 160, 178, 175, 175, 175, 175, - /* 1050 */ 159, 159, 176, 195, 175, 175, 113, 46, 107, 118, - /* 1060 */ 116, 115, 185, 214, 117, 180, 214, 118, 114, 181, - /* 1070 */ 25, 182, 94, 160, 26, 151, 183, 109, 200, 184, - /* 1080 */ 109, 139, 81, 89, 107, 126, 107, 139, 17, 139, - /* 1090 */ 107, 22, 198, 174, 109, 23, 139, 23, 25, 143, - /* 1100 */ 139, 198, 114, 25, 208, 190, 209, 111, 139, 139, - /* 1110 */ 143, 139, 160, 139, 191, 95, 22, 112, 192, 139, - /* 1120 */ 23, 191, 109, 23, 22, 192, 139, 165, 162, 139, - /* 1130 */ 167, 23, 159, 198, 198, 46, 22, 22, 46, 163, - /* 1140 */ 22, 100, 93, 24, 217, 139, 151, 139, 95, 39, - /* 1150 */ 166, 152, 166, 160, 220, 166, 219, 160, 11, 143, - /* 1160 */ 139, 139, 139, 37, 47, 95, 159, 169, 159, 143, - /* 1170 */ 143, 169, 162, 143, 95, 163, 218, 139, 143, 129, - /* 1180 */ 95, 22, 9, 159, 129, 11, 172, 119, 17, 9, - /* 1190 */ 9, 58, 17, 139, 119, 99, 139, 172, 67, 181, - /* 1200 */ 9, 67, 119, 139, 22, 22, 9, 9, 110, 9, - /* 1210 */ 181, 9, 9, 9, 110, 139, 9, 181, 172, 99, - /* 1220 */ 181, 9, 119, 22, 9, 139, 9, 139, 9, 9, - /* 1230 */ 202, 9, 9, 202, 143, 9, 23, 9, 139, 34, - /* 1240 */ 24, 9, 152, 9, 139, 152, 139, 9, 224, 224, - /* 1250 */ 224, 224, 224, 224, 222, 224, 223, 224, 224, 222, -}; -#define YY_SHIFT_USE_DFLT (-70) -static const short yy_shift_ofst[] = { - /* 0 */ 430, 21, -70, 834, 71, -70, 247, 214, 145, 304, - /* 10 */ 292, 620, -70, -70, -70, -70, -70, -70, 145, 662, - /* 20 */ 145, 856, 145, 964, 36, 1015, 245, 46, 1004, 1019, - /* 30 */ -9, -70, 675, -70, 215, -70, 245, -27, -70, 940, - /* 40 */ -70, 1003, 170, -70, -70, -70, -70, -70, -70, -70, - /* 50 */ 286, 940, -70, 991, -70, 517, -70, -70, 992, 105, - /* 60 */ 940, -70, -70, -70, 940, -70, 1017, 862, 376, 650, - /* 70 */ 931, 932, 680, -70, 120, 951, -70, 166, -70, 554, - /* 80 */ 941, 946, 944, 943, 947, -70, 497, -70, -70, 767, - /* 90 */ 497, -70, 499, -70, -70, -70, 499, -70, -70, 497, - /* 100 */ -70, 954, 862, 1045, 862, 978, 105, -70, 1048, -70, - /* 110 */ -70, 483, 862, -70, 968, 245, 971, 245, -70, -70, - /* 120 */ -70, -70, -70, 618, 862, 573, 862, -69, 862, -69, - /* 130 */ 862, -69, 862, -69, 862, 49, 862, 49, 862, 9, - /* 140 */ 862, 9, 862, 9, 862, 9, 862, 301, 862, 301, - /* 150 */ 862, 1001, 862, 1001, 862, 1001, 862, -70, -70, -70, - /* 160 */ 679, -70, -70, -70, -70, -70, 862, 49, -70, 571, - /* 170 */ -70, 994, -70, -70, -70, 862, 528, 862, 49, -70, - /* 180 */ 127, 680, 298, 228, 977, 979, 983, -70, 483, 862, - /* 190 */ 618, 862, -70, 862, -70, 862, -70, 736, 35, 959, - /* 200 */ 322, 1071, -70, 862, 104, 862, 483, 1069, 691, 1072, - /* 210 */ -70, 1073, 245, 1074, -70, 862, 174, 862, 219, 862, - /* 220 */ 483, 167, -70, 862, -70, -70, 985, 245, -70, -70, - /* 230 */ 978, 105, -70, 862, 483, 988, 862, 1078, 862, 483, - /* 240 */ -70, -70, 652, -70, -70, -70, 113, -70, 409, -70, - /* 250 */ 996, -70, 242, 985, 588, -70, -70, 245, -70, -70, - /* 260 */ 1020, 1005, -70, 1094, 245, 644, -70, 245, -70, -70, - /* 270 */ 862, 483, 951, 374, 108, 1097, 588, 1020, 1005, -70, - /* 280 */ 757, -24, -70, -70, 1013, 358, -70, -70, -70, -70, - /* 290 */ 289, -70, 772, -70, 1100, -70, 348, 940, -70, 245, - /* 300 */ 1102, -70, 227, -70, 245, -70, 529, 701, -70, 749, - /* 310 */ -70, -70, -70, -70, 701, -70, 701, -70, 245, 938, - /* 320 */ -70, 245, 978, 105, -70, -70, 978, 105, -70, -70, - /* 330 */ 1048, -70, 991, -70, -70, 184, -70, -70, -70, -70, - /* 340 */ 589, 497, 939, -70, 497, 1108, -70, -70, -70, -70, - /* 350 */ 45, 233, -70, 245, -70, 1089, 1114, 245, 945, 940, - /* 360 */ -70, 1115, 245, 955, 940, -70, 862, 393, -70, 1092, - /* 370 */ 1118, 245, 965, 1049, 245, 1102, -70, 162, 1041, -70, - /* 380 */ -70, -70, -70, -70, 951, 423, 305, 692, 245, 985, - /* 390 */ -70, 245, 886, 1119, 951, 429, 245, 985, 783, 395, - /* 400 */ 1053, 245, 985, -70, 1110, 780, 1147, 862, 438, 1126, - /* 410 */ 846, -70, -70, 1070, 1079, 490, 245, 682, -70, -70, - /* 420 */ 1117, -70, -70, 1050, 245, 887, 1085, 245, 1159, 245, - /* 430 */ 975, 752, 1173, 1055, 1174, 169, 433, 200, 170, -70, - /* 440 */ 1068, 1075, 1171, 1180, 1181, 169, 1175, 1133, 245, 1096, - /* 450 */ 245, 769, 245, 1131, 862, 483, 1191, 1134, 862, 483, - /* 460 */ 1083, 245, 1182, 245, 981, -70, 711, 472, 1183, 862, - /* 470 */ 982, 862, 483, 1197, 483, 1098, 245, 949, 1198, 602, - /* 480 */ 245, 1200, 245, 1202, 245, 1203, 245, 1204, 478, 1104, - /* 490 */ 245, 949, 1207, 1133, 245, 1120, 245, 769, 1212, 1103, - /* 500 */ 245, 1182, 902, 513, 1201, 862, 1000, 1215, 695, 1217, - /* 510 */ 245, 985, 601, 65, 1219, 1220, 1222, 1223, 245, 1213, - /* 520 */ 1226, 1205, 675, 1216, 245, 1011, 1228, 629, 1232, 1234, - /* 530 */ -70, 1205, 245, 1238, -70, -70, -70, -}; -#define YY_REDUCE_USE_DFLT (-142) -static const short yy_reduce_ofst[] = { - /* 0 */ -107, 342, -142, -117, -142, -142, -142, 72, 442, -142, - /* 10 */ 394, -142, -142, -142, -142, -142, -142, -142, 525, -142, - /* 20 */ 579, -142, 731, -142, 515, -142, 8, 881, -142, -142, - /* 30 */ 48, -142, 337, 882, 153, -142, 890, -136, -142, 875, - /* 40 */ -142, -142, 310, -142, -142, -142, -142, -142, -142, -142, - /* 50 */ -142, 883, -142, 870, -142, -142, -142, -142, -142, 884, - /* 60 */ 891, -142, -142, -142, 892, -142, -142, 693, -142, 175, - /* 70 */ -142, -142, 54, -142, 866, 876, -142, 867, -41, 885, - /* 80 */ 888, 889, 893, 895, 877, -142, -141, -142, -142, -142, - /* 90 */ 186, -142, 849, -142, -142, -142, 852, -142, -142, 189, - /* 100 */ -142, -142, 234, -142, 244, 894, 913, -142, 924, -142, - /* 110 */ -142, 241, 705, -142, -142, 942, -142, 948, -142, -142, - /* 120 */ -142, -142, -142, 241, 716, 241, 732, 241, 734, 241, - /* 130 */ 735, 241, 737, 241, 747, 241, 750, 241, 753, 241, - /* 140 */ 755, 241, 758, 241, 763, 241, 764, 241, 765, 241, - /* 150 */ 766, 241, 768, 241, 774, 241, 776, 241, -142, -142, - /* 160 */ -142, -142, -142, -142, -142, -142, 779, 241, -142, -142, - /* 170 */ -142, -142, -142, -142, -142, 781, 241, 782, 241, -142, - /* 180 */ 950, 609, 866, -142, -142, -142, -142, -142, 241, 784, - /* 190 */ 241, 789, 241, 794, 241, 795, 241, 583, 241, 896, - /* 200 */ 897, -142, -142, 805, 241, 810, 241, -142, 919, -142, - /* 210 */ -142, -142, 957, -142, -142, 813, 241, 814, 241, 815, - /* 220 */ 241, -142, -142, 606, -142, -142, 956, 961, -142, -142, - /* 230 */ 903, 952, -142, 818, 241, -142, 177, -142, 820, 241, - /* 240 */ -142, 477, 915, -142, -142, -142, 969, -142, 970, -142, - /* 250 */ -142, -142, 972, 967, 518, -142, -142, 974, -142, -142, - /* 260 */ 923, 926, -142, -142, 821, -142, -142, 980, -142, -142, - /* 270 */ 828, 241, 13, 866, 915, -142, 564, 930, 933, -142, - /* 280 */ 830, 185, -142, -142, -142, 942, -142, -142, -142, -142, - /* 290 */ 241, -142, -142, -142, -142, -142, 241, 973, -142, 987, - /* 300 */ 966, 976, 962, -142, 990, -142, -142, 984, -142, -142, - /* 310 */ -142, -142, -142, -142, 986, -142, 989, -142, -138, -142, - /* 320 */ -142, 689, 935, 993, -142, -142, 936, 997, -142, -142, - /* 330 */ 995, -142, 963, -142, -142, 369, -142, -142, -142, -142, - /* 340 */ 999, 411, -142, -142, 415, -142, -142, -142, -142, -142, - /* 350 */ 998, 1002, -142, 1006, -142, -142, -142, 491, -142, 1007, - /* 360 */ -142, -142, 576, -142, 1009, -142, 833, -1, -142, -142, - /* 370 */ -142, 660, -142, -142, 1008, 1010, 1012, 229, -142, -142, - /* 380 */ -142, -142, -142, -142, 567, 866, 595, -142, 1021, 1016, - /* 390 */ -142, 1022, 1026, -142, 574, 866, 1023, 1027, 927, 958, - /* 400 */ -142, 401, 1030, -142, 937, 934, -142, 838, 241, -142, - /* 410 */ -142, -142, -142, -142, -142, -142, 826, -142, -142, -142, - /* 420 */ -142, -142, -142, -142, 1038, 1035, -142, 536, -142, 697, - /* 430 */ -142, 1024, -142, -142, -142, 608, 866, 1014, 829, -142, - /* 440 */ -142, -142, -142, -142, -142, 647, -142, 1025, 1054, -142, - /* 450 */ 717, 1018, 1057, -142, 853, 241, -142, -142, 854, 241, - /* 460 */ -142, 1064, 1028, 858, -142, -142, 613, 866, -142, 637, - /* 470 */ -142, 869, 241, -142, 241, -142, 1076, 1029, -142, -142, - /* 480 */ 700, -142, 871, -142, 872, -142, 873, -142, 866, -142, - /* 490 */ 874, 1036, -142, 1046, 879, -142, 878, 1039, -142, -142, - /* 500 */ 880, 1031, 855, 866, -142, 688, -142, -142, 1086, -142, - /* 510 */ 1088, 1091, -142, 569, -142, -142, -142, -142, 1099, -142, - /* 520 */ -142, 1032, 1090, -142, 1105, 1033, -142, 1093, -142, -142, - /* 530 */ -142, 1037, 1107, -142, -142, -142, -142, -}; -static const YYACTIONTYPE yy_default[] = { - /* 0 */ 544, 544, 538, 829, 829, 540, 829, 549, 829, 829, - /* 10 */ 829, 829, 569, 570, 571, 550, 551, 552, 829, 829, - /* 20 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, - /* 30 */ 829, 562, 572, 581, 564, 580, 829, 829, 582, 623, - /* 40 */ 588, 829, 829, 624, 627, 628, 629, 818, 819, 820, - /* 50 */ 829, 623, 589, 608, 606, 829, 609, 610, 829, 679, - /* 60 */ 623, 590, 677, 678, 623, 591, 829, 829, 708, 770, - /* 70 */ 714, 709, 829, 634, 829, 829, 635, 643, 645, 652, - /* 80 */ 691, 682, 684, 672, 686, 640, 793, 578, 579, 687, - /* 90 */ 793, 688, 829, 788, 790, 791, 829, 789, 792, 793, - /* 100 */ 689, 829, 829, 673, 829, 680, 679, 674, 829, 566, - /* 110 */ 681, 676, 829, 707, 829, 829, 710, 829, 711, 712, - /* 120 */ 713, 715, 716, 719, 829, 720, 829, 721, 829, 722, - /* 130 */ 829, 723, 829, 724, 829, 725, 829, 726, 829, 727, - /* 140 */ 829, 728, 829, 729, 829, 730, 829, 731, 829, 732, - /* 150 */ 829, 733, 829, 734, 829, 735, 829, 736, 737, 738, - /* 160 */ 829, 739, 740, 745, 753, 756, 829, 741, 742, 829, - /* 170 */ 743, 829, 746, 744, 752, 829, 829, 829, 754, 755, - /* 180 */ 829, 770, 829, 829, 829, 829, 829, 758, 769, 829, - /* 190 */ 747, 829, 748, 829, 749, 829, 750, 829, 829, 829, - /* 200 */ 829, 829, 760, 829, 829, 829, 761, 829, 829, 829, - /* 210 */ 816, 829, 829, 829, 817, 829, 829, 829, 829, 829, - /* 220 */ 762, 829, 757, 770, 767, 768, 660, 829, 661, 759, - /* 230 */ 680, 679, 675, 829, 685, 829, 770, 683, 829, 692, - /* 240 */ 644, 655, 653, 654, 663, 664, 829, 665, 829, 666, - /* 250 */ 829, 667, 829, 660, 651, 567, 568, 829, 649, 650, - /* 260 */ 669, 671, 656, 829, 829, 829, 670, 829, 704, 705, - /* 270 */ 829, 668, 655, 829, 829, 829, 651, 669, 671, 657, - /* 280 */ 829, 651, 646, 647, 829, 829, 648, 641, 642, 751, - /* 290 */ 829, 706, 829, 717, 829, 718, 829, 623, 592, 829, - /* 300 */ 774, 596, 593, 597, 829, 598, 829, 829, 599, 829, - /* 310 */ 602, 603, 604, 605, 829, 600, 829, 601, 829, 829, - /* 320 */ 775, 829, 680, 679, 776, 778, 680, 679, 777, 594, - /* 330 */ 829, 595, 608, 607, 583, 793, 584, 585, 586, 587, - /* 340 */ 573, 793, 829, 574, 793, 829, 575, 577, 576, 565, - /* 350 */ 829, 829, 613, 829, 616, 829, 829, 829, 829, 623, - /* 360 */ 617, 829, 829, 829, 623, 618, 829, 623, 619, 829, - /* 370 */ 829, 829, 829, 829, 829, 774, 596, 621, 829, 620, - /* 380 */ 622, 614, 615, 563, 829, 829, 559, 829, 829, 660, - /* 390 */ 557, 829, 829, 829, 829, 829, 829, 660, 799, 829, - /* 400 */ 829, 829, 660, 662, 804, 829, 829, 829, 829, 829, - /* 410 */ 829, 805, 806, 829, 829, 829, 829, 829, 796, 797, - /* 420 */ 829, 798, 558, 829, 829, 829, 829, 829, 829, 829, - /* 430 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 626, - /* 440 */ 829, 829, 829, 829, 829, 829, 829, 625, 829, 829, - /* 450 */ 829, 829, 829, 829, 829, 694, 829, 829, 829, 695, - /* 460 */ 829, 829, 702, 829, 829, 703, 829, 829, 829, 829, - /* 470 */ 829, 829, 700, 829, 701, 829, 829, 829, 829, 829, - /* 480 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, - /* 490 */ 829, 829, 829, 625, 829, 829, 829, 829, 829, 829, - /* 500 */ 829, 702, 829, 829, 829, 829, 829, 829, 829, 829, - /* 510 */ 829, 660, 829, 793, 829, 829, 829, 829, 829, 829, - /* 520 */ 829, 827, 829, 829, 829, 829, 829, 829, 829, 829, - /* 530 */ 826, 827, 829, 829, 541, 543, 539, -}; -#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) - -/* The next table maps tokens into fallback tokens. If a construct -** like the following: -** -** %fallback ID X Y Z. -** -** appears in the grammer, then ID becomes a fallback token for X, Y, -** and Z. Whenever one of the tokens X, Y, or Z is input to the parser -** but it does not parse, the type of the token is changed to ID and -** the parse is retried before an error is thrown. -*/ -#ifdef YYFALLBACK -static const YYCODETYPE yyFallback[] = { - 0, /* $ => nothing */ - 0, /* END_OF_FILE => nothing */ - 0, /* ILLEGAL => nothing */ - 0, /* SPACE => nothing */ - 0, /* UNCLOSED_STRING => nothing */ - 0, /* COMMENT => nothing */ - 0, /* FUNCTION => nothing */ - 0, /* COLUMN => nothing */ - 0, /* AGG_FUNCTION => nothing */ - 0, /* SEMI => nothing */ - 26, /* EXPLAIN => ID */ - 26, /* BEGIN => ID */ - 0, /* TRANSACTION => nothing */ - 26, /* DEFERRED => ID */ - 26, /* IMMEDIATE => ID */ - 26, /* EXCLUSIVE => ID */ - 0, /* COMMIT => nothing */ - 26, /* END => ID */ - 0, /* ROLLBACK => nothing */ - 0, /* CREATE => nothing */ - 0, /* TABLE => nothing */ - 26, /* TEMP => ID */ - 0, /* LP => nothing */ - 0, /* RP => nothing */ - 0, /* AS => nothing */ - 0, /* COMMA => nothing */ - 0, /* ID => nothing */ - 26, /* ABORT => ID */ - 26, /* AFTER => ID */ - 26, /* ASC => ID */ - 26, /* ATTACH => ID */ - 26, /* BEFORE => ID */ - 26, /* CASCADE => ID */ - 26, /* CONFLICT => ID */ - 26, /* DATABASE => ID */ - 26, /* DESC => ID */ - 26, /* DETACH => ID */ - 26, /* EACH => ID */ - 26, /* FAIL => ID */ - 26, /* FOR => ID */ - 26, /* GLOB => ID */ - 26, /* IGNORE => ID */ - 26, /* INITIALLY => ID */ - 26, /* INSTEAD => ID */ - 26, /* LIKE => ID */ - 26, /* MATCH => ID */ - 26, /* KEY => ID */ - 26, /* OF => ID */ - 26, /* OFFSET => ID */ - 26, /* PRAGMA => ID */ - 26, /* RAISE => ID */ - 26, /* REPLACE => ID */ - 26, /* RESTRICT => ID */ - 26, /* ROW => ID */ - 26, /* STATEMENT => ID */ - 26, /* TRIGGER => ID */ - 26, /* VACUUM => ID */ - 26, /* VIEW => ID */ - 0, /* OR => nothing */ - 0, /* AND => nothing */ - 0, /* NOT => nothing */ - 0, /* IS => nothing */ - 0, /* BETWEEN => nothing */ - 0, /* IN => nothing */ - 0, /* ISNULL => nothing */ - 0, /* NOTNULL => nothing */ - 0, /* NE => nothing */ - 0, /* EQ => nothing */ - 0, /* GT => nothing */ - 0, /* LE => nothing */ - 0, /* LT => nothing */ - 0, /* GE => nothing */ - 0, /* BITAND => nothing */ - 0, /* BITOR => nothing */ - 0, /* LSHIFT => nothing */ - 0, /* RSHIFT => nothing */ - 0, /* PLUS => nothing */ - 0, /* MINUS => nothing */ - 0, /* STAR => nothing */ - 0, /* SLASH => nothing */ - 0, /* REM => nothing */ - 0, /* CONCAT => nothing */ - 0, /* UMINUS => nothing */ - 0, /* UPLUS => nothing */ - 0, /* BITNOT => nothing */ - 0, /* STRING => nothing */ - 0, /* JOIN_KW => nothing */ - 0, /* CONSTRAINT => nothing */ - 0, /* DEFAULT => nothing */ - 0, /* NULL => nothing */ - 0, /* PRIMARY => nothing */ - 0, /* UNIQUE => nothing */ - 0, /* CHECK => nothing */ - 0, /* REFERENCES => nothing */ - 0, /* COLLATE => nothing */ - 0, /* ON => nothing */ - 0, /* DELETE => nothing */ - 0, /* UPDATE => nothing */ - 0, /* INSERT => nothing */ - 0, /* SET => nothing */ - 0, /* DEFERRABLE => nothing */ - 0, /* FOREIGN => nothing */ - 0, /* DROP => nothing */ - 0, /* UNION => nothing */ - 0, /* ALL => nothing */ - 0, /* INTERSECT => nothing */ - 0, /* EXCEPT => nothing */ - 0, /* SELECT => nothing */ - 0, /* DISTINCT => nothing */ - 0, /* DOT => nothing */ - 0, /* FROM => nothing */ - 0, /* JOIN => nothing */ - 0, /* USING => nothing */ - 0, /* ORDER => nothing */ - 0, /* BY => nothing */ - 0, /* GROUP => nothing */ - 0, /* HAVING => nothing */ - 0, /* LIMIT => nothing */ - 0, /* WHERE => nothing */ - 0, /* INTO => nothing */ - 0, /* VALUES => nothing */ - 0, /* INTEGER => nothing */ - 0, /* FLOAT => nothing */ - 0, /* BLOB => nothing */ - 0, /* VARIABLE => nothing */ - 0, /* CASE => nothing */ - 0, /* WHEN => nothing */ - 0, /* THEN => nothing */ - 0, /* ELSE => nothing */ - 0, /* INDEX => nothing */ -}; -#endif /* YYFALLBACK */ - -/* The following structure represents a single element of the -** parser's stack. Information stored includes: -** -** + The state number for the parser at this level of the stack. -** -** + The value of the token stored at this level of the stack. -** (In other words, the "major" token.) -** -** + The semantic value stored at this level of the stack. This is -** the information used by the action routines in the grammar. -** It is sometimes called the "minor" token. -*/ -struct yyStackEntry { - int stateno; /* The state-number */ - int major; /* The major token value. This is the code - ** number for the token at this stack level */ - YYMINORTYPE minor; /* The user-supplied minor token value. This - ** is the value of the token */ -}; -typedef struct yyStackEntry yyStackEntry; - -/* The state of the parser is completely contained in an instance of -** the following structure */ -struct yyParser { - int yyidx; /* Index of top element in stack */ - int yyerrcnt; /* Shifts left before out of the error */ - sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ - yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ -}; -typedef struct yyParser yyParser; - -#ifndef NDEBUG -#include <stdio.h> -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* -** Turn parser tracing on by giving a stream to which to write the trace -** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL -** -** Inputs: -** <ul> -** <li> A FILE* to which trace output should be written. -** If NULL, then tracing is turned off. -** <li> A prefix string written at the beginning of every -** line of trace output. If NULL, then tracing is -** turned off. -** </ul> -** -** Outputs: -** None. -*/ -void sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){ - yyTraceFILE = TraceFILE; - yyTracePrompt = zTracePrompt; - if( yyTraceFILE==0 ) yyTracePrompt = 0; - else if( yyTracePrompt==0 ) yyTraceFILE = 0; -} -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing shifts, the names of all terminals and nonterminals -** are required. The following table supplies these names */ -static const char *const yyTokenName[] = { - "$", "END_OF_FILE", "ILLEGAL", "SPACE", - "UNCLOSED_STRING", "COMMENT", "FUNCTION", "COLUMN", - "AGG_FUNCTION", "SEMI", "EXPLAIN", "BEGIN", - "TRANSACTION", "DEFERRED", "IMMEDIATE", "EXCLUSIVE", - "COMMIT", "END", "ROLLBACK", "CREATE", - "TABLE", "TEMP", "LP", "RP", - "AS", "COMMA", "ID", "ABORT", - "AFTER", "ASC", "ATTACH", "BEFORE", - "CASCADE", "CONFLICT", "DATABASE", "DESC", - "DETACH", "EACH", "FAIL", "FOR", - "GLOB", "IGNORE", "INITIALLY", "INSTEAD", - "LIKE", "MATCH", "KEY", "OF", - "OFFSET", "PRAGMA", "RAISE", "REPLACE", - "RESTRICT", "ROW", "STATEMENT", "TRIGGER", - "VACUUM", "VIEW", "OR", "AND", - "NOT", "IS", "BETWEEN", "IN", - "ISNULL", "NOTNULL", "NE", "EQ", - "GT", "LE", "LT", "GE", - "BITAND", "BITOR", "LSHIFT", "RSHIFT", - "PLUS", "MINUS", "STAR", "SLASH", - "REM", "CONCAT", "UMINUS", "UPLUS", - "BITNOT", "STRING", "JOIN_KW", "CONSTRAINT", - "DEFAULT", "NULL", "PRIMARY", "UNIQUE", - "CHECK", "REFERENCES", "COLLATE", "ON", - "DELETE", "UPDATE", "INSERT", "SET", - "DEFERRABLE", "FOREIGN", "DROP", "UNION", - "ALL", "INTERSECT", "EXCEPT", "SELECT", - "DISTINCT", "DOT", "FROM", "JOIN", - "USING", "ORDER", "BY", "GROUP", - "HAVING", "LIMIT", "WHERE", "INTO", - "VALUES", "INTEGER", "FLOAT", "BLOB", - "VARIABLE", "CASE", "WHEN", "THEN", - "ELSE", "INDEX", "error", "input", - "cmdlist", "ecmd", "explain", "cmdx", - "cmd", "transtype", "trans_opt", "nm", - "create_table", "create_table_args", "temp", "dbnm", - "columnlist", "conslist_opt", "select", "column", - "columnid", "type", "carglist", "id", - "ids", "typename", "signed", "plus_num", - "minus_num", "carg", "ccons", "onconf", - "sortorder", "expr", "idxlist_opt", "refargs", - "defer_subclause", "refarg", "refact", "init_deferred_pred_opt", - "conslist", "tcons", "idxlist", "defer_subclause_opt", - "orconf", "resolvetype", "raisetype", "fullname", - "oneselect", "multiselect_op", "distinct", "selcollist", - "from", "where_opt", "groupby_opt", "having_opt", - "orderby_opt", "limit_opt", "sclp", "as", - "seltablist", "stl_prefix", "joinop", "on_opt", - "using_opt", "seltablist_paren", "joinop2", "inscollist", - "sortlist", "sortitem", "collate", "exprlist", - "setlist", "insert_cmd", "inscollist_opt", "itemlist", - "likeop", "between_op", "in_op", "case_operand", - "case_exprlist", "case_else", "expritem", "uniqueflag", - "idxitem", "plus_opt", "number", "trigger_decl", - "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause", - "when_clause", "trigger_cmd", "database_kw_opt", "key_opt", -}; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing reduce actions, the names of all rules are required. -*/ -static const char *const yyRuleName[] = { - /* 0 */ "input ::= cmdlist", - /* 1 */ "cmdlist ::= cmdlist ecmd", - /* 2 */ "cmdlist ::= ecmd", - /* 3 */ "ecmd ::= explain cmdx SEMI", - /* 4 */ "ecmd ::= SEMI", - /* 5 */ "cmdx ::= cmd", - /* 6 */ "explain ::= EXPLAIN", - /* 7 */ "explain ::=", - /* 8 */ "cmd ::= BEGIN transtype trans_opt", - /* 9 */ "trans_opt ::=", - /* 10 */ "trans_opt ::= TRANSACTION", - /* 11 */ "trans_opt ::= TRANSACTION nm", - /* 12 */ "transtype ::=", - /* 13 */ "transtype ::= DEFERRED", - /* 14 */ "transtype ::= IMMEDIATE", - /* 15 */ "transtype ::= EXCLUSIVE", - /* 16 */ "cmd ::= COMMIT trans_opt", - /* 17 */ "cmd ::= END trans_opt", - /* 18 */ "cmd ::= ROLLBACK trans_opt", - /* 19 */ "cmd ::= create_table create_table_args", - /* 20 */ "create_table ::= CREATE temp TABLE nm dbnm", - /* 21 */ "temp ::= TEMP", - /* 22 */ "temp ::=", - /* 23 */ "create_table_args ::= LP columnlist conslist_opt RP", - /* 24 */ "create_table_args ::= AS select", - /* 25 */ "columnlist ::= columnlist COMMA column", - /* 26 */ "columnlist ::= column", - /* 27 */ "column ::= columnid type carglist", - /* 28 */ "columnid ::= nm", - /* 29 */ "id ::= ID", - /* 30 */ "ids ::= ID", - /* 31 */ "ids ::= STRING", - /* 32 */ "nm ::= ID", - /* 33 */ "nm ::= STRING", - /* 34 */ "nm ::= JOIN_KW", - /* 35 */ "type ::=", - /* 36 */ "type ::= typename", - /* 37 */ "type ::= typename LP signed RP", - /* 38 */ "type ::= typename LP signed COMMA signed RP", - /* 39 */ "typename ::= ids", - /* 40 */ "typename ::= typename ids", - /* 41 */ "signed ::= plus_num", - /* 42 */ "signed ::= minus_num", - /* 43 */ "carglist ::= carglist carg", - /* 44 */ "carglist ::=", - /* 45 */ "carg ::= CONSTRAINT nm ccons", - /* 46 */ "carg ::= ccons", - /* 47 */ "carg ::= DEFAULT ids", - /* 48 */ "carg ::= DEFAULT plus_num", - /* 49 */ "carg ::= DEFAULT minus_num", - /* 50 */ "carg ::= DEFAULT NULL", - /* 51 */ "ccons ::= NULL onconf", - /* 52 */ "ccons ::= NOT NULL onconf", - /* 53 */ "ccons ::= PRIMARY KEY sortorder onconf", - /* 54 */ "ccons ::= UNIQUE onconf", - /* 55 */ "ccons ::= CHECK LP expr RP onconf", - /* 56 */ "ccons ::= REFERENCES nm idxlist_opt refargs", - /* 57 */ "ccons ::= defer_subclause", - /* 58 */ "ccons ::= COLLATE id", - /* 59 */ "refargs ::=", - /* 60 */ "refargs ::= refargs refarg", - /* 61 */ "refarg ::= MATCH nm", - /* 62 */ "refarg ::= ON DELETE refact", - /* 63 */ "refarg ::= ON UPDATE refact", - /* 64 */ "refarg ::= ON INSERT refact", - /* 65 */ "refact ::= SET NULL", - /* 66 */ "refact ::= SET DEFAULT", - /* 67 */ "refact ::= CASCADE", - /* 68 */ "refact ::= RESTRICT", - /* 69 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 70 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 71 */ "init_deferred_pred_opt ::=", - /* 72 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 73 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 74 */ "conslist_opt ::=", - /* 75 */ "conslist_opt ::= COMMA conslist", - /* 76 */ "conslist ::= conslist COMMA tcons", - /* 77 */ "conslist ::= conslist tcons", - /* 78 */ "conslist ::= tcons", - /* 79 */ "tcons ::= CONSTRAINT nm", - /* 80 */ "tcons ::= PRIMARY KEY LP idxlist RP onconf", - /* 81 */ "tcons ::= UNIQUE LP idxlist RP onconf", - /* 82 */ "tcons ::= CHECK expr onconf", - /* 83 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", - /* 84 */ "defer_subclause_opt ::=", - /* 85 */ "defer_subclause_opt ::= defer_subclause", - /* 86 */ "onconf ::=", - /* 87 */ "onconf ::= ON CONFLICT resolvetype", - /* 88 */ "orconf ::=", - /* 89 */ "orconf ::= OR resolvetype", - /* 90 */ "resolvetype ::= raisetype", - /* 91 */ "resolvetype ::= IGNORE", - /* 92 */ "resolvetype ::= REPLACE", - /* 93 */ "cmd ::= DROP TABLE fullname", - /* 94 */ "cmd ::= CREATE temp VIEW nm dbnm AS select", - /* 95 */ "cmd ::= DROP VIEW fullname", - /* 96 */ "cmd ::= select", - /* 97 */ "select ::= oneselect", - /* 98 */ "select ::= select multiselect_op oneselect", - /* 99 */ "multiselect_op ::= UNION", - /* 100 */ "multiselect_op ::= UNION ALL", - /* 101 */ "multiselect_op ::= INTERSECT", - /* 102 */ "multiselect_op ::= EXCEPT", - /* 103 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 104 */ "distinct ::= DISTINCT", - /* 105 */ "distinct ::= ALL", - /* 106 */ "distinct ::=", - /* 107 */ "sclp ::= selcollist COMMA", - /* 108 */ "sclp ::=", - /* 109 */ "selcollist ::= sclp expr as", - /* 110 */ "selcollist ::= sclp STAR", - /* 111 */ "selcollist ::= sclp nm DOT STAR", - /* 112 */ "as ::= AS nm", - /* 113 */ "as ::= ids", - /* 114 */ "as ::=", - /* 115 */ "from ::=", - /* 116 */ "from ::= FROM seltablist", - /* 117 */ "stl_prefix ::= seltablist joinop", - /* 118 */ "stl_prefix ::=", - /* 119 */ "seltablist ::= stl_prefix nm dbnm as on_opt using_opt", - /* 120 */ "seltablist ::= stl_prefix LP seltablist_paren RP as on_opt using_opt", - /* 121 */ "seltablist_paren ::= select", - /* 122 */ "seltablist_paren ::= seltablist", - /* 123 */ "dbnm ::=", - /* 124 */ "dbnm ::= DOT nm", - /* 125 */ "fullname ::= nm dbnm", - /* 126 */ "joinop ::= COMMA", - /* 127 */ "joinop ::= JOIN", - /* 128 */ "joinop ::= JOIN_KW JOIN", - /* 129 */ "joinop ::= JOIN_KW nm JOIN", - /* 130 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 131 */ "on_opt ::= ON expr", - /* 132 */ "on_opt ::=", - /* 133 */ "using_opt ::= USING LP inscollist RP", - /* 134 */ "using_opt ::=", - /* 135 */ "orderby_opt ::=", - /* 136 */ "orderby_opt ::= ORDER BY sortlist", - /* 137 */ "sortlist ::= sortlist COMMA sortitem collate sortorder", - /* 138 */ "sortlist ::= sortitem collate sortorder", - /* 139 */ "sortitem ::= expr", - /* 140 */ "sortorder ::= ASC", - /* 141 */ "sortorder ::= DESC", - /* 142 */ "sortorder ::=", - /* 143 */ "collate ::=", - /* 144 */ "collate ::= COLLATE id", - /* 145 */ "groupby_opt ::=", - /* 146 */ "groupby_opt ::= GROUP BY exprlist", - /* 147 */ "having_opt ::=", - /* 148 */ "having_opt ::= HAVING expr", - /* 149 */ "limit_opt ::=", - /* 150 */ "limit_opt ::= LIMIT signed", - /* 151 */ "limit_opt ::= LIMIT signed OFFSET signed", - /* 152 */ "limit_opt ::= LIMIT signed COMMA signed", - /* 153 */ "cmd ::= DELETE FROM fullname where_opt", - /* 154 */ "where_opt ::=", - /* 155 */ "where_opt ::= WHERE expr", - /* 156 */ "cmd ::= UPDATE orconf fullname SET setlist where_opt", - /* 157 */ "setlist ::= setlist COMMA nm EQ expr", - /* 158 */ "setlist ::= nm EQ expr", - /* 159 */ "cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP", - /* 160 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select", - /* 161 */ "insert_cmd ::= INSERT orconf", - /* 162 */ "insert_cmd ::= REPLACE", - /* 163 */ "itemlist ::= itemlist COMMA expr", - /* 164 */ "itemlist ::= expr", - /* 165 */ "inscollist_opt ::=", - /* 166 */ "inscollist_opt ::= LP inscollist RP", - /* 167 */ "inscollist ::= inscollist COMMA nm", - /* 168 */ "inscollist ::= nm", - /* 169 */ "expr ::= LP expr RP", - /* 170 */ "expr ::= NULL", - /* 171 */ "expr ::= ID", - /* 172 */ "expr ::= JOIN_KW", - /* 173 */ "expr ::= nm DOT nm", - /* 174 */ "expr ::= nm DOT nm DOT nm", - /* 175 */ "expr ::= INTEGER", - /* 176 */ "expr ::= FLOAT", - /* 177 */ "expr ::= STRING", - /* 178 */ "expr ::= BLOB", - /* 179 */ "expr ::= VARIABLE", - /* 180 */ "expr ::= ID LP exprlist RP", - /* 181 */ "expr ::= ID LP STAR RP", - /* 182 */ "expr ::= expr AND expr", - /* 183 */ "expr ::= expr OR expr", - /* 184 */ "expr ::= expr LT expr", - /* 185 */ "expr ::= expr GT expr", - /* 186 */ "expr ::= expr LE expr", - /* 187 */ "expr ::= expr GE expr", - /* 188 */ "expr ::= expr NE expr", - /* 189 */ "expr ::= expr EQ expr", - /* 190 */ "expr ::= expr BITAND expr", - /* 191 */ "expr ::= expr BITOR expr", - /* 192 */ "expr ::= expr LSHIFT expr", - /* 193 */ "expr ::= expr RSHIFT expr", - /* 194 */ "expr ::= expr PLUS expr", - /* 195 */ "expr ::= expr MINUS expr", - /* 196 */ "expr ::= expr STAR expr", - /* 197 */ "expr ::= expr SLASH expr", - /* 198 */ "expr ::= expr REM expr", - /* 199 */ "expr ::= expr CONCAT expr", - /* 200 */ "likeop ::= LIKE", - /* 201 */ "likeop ::= GLOB", - /* 202 */ "likeop ::= NOT LIKE", - /* 203 */ "likeop ::= NOT GLOB", - /* 204 */ "expr ::= expr likeop expr", - /* 205 */ "expr ::= expr ISNULL", - /* 206 */ "expr ::= expr IS NULL", - /* 207 */ "expr ::= expr NOTNULL", - /* 208 */ "expr ::= expr NOT NULL", - /* 209 */ "expr ::= expr IS NOT NULL", - /* 210 */ "expr ::= NOT expr", - /* 211 */ "expr ::= BITNOT expr", - /* 212 */ "expr ::= MINUS expr", - /* 213 */ "expr ::= PLUS expr", - /* 214 */ "expr ::= LP select RP", - /* 215 */ "between_op ::= BETWEEN", - /* 216 */ "between_op ::= NOT BETWEEN", - /* 217 */ "expr ::= expr between_op expr AND expr", - /* 218 */ "in_op ::= IN", - /* 219 */ "in_op ::= NOT IN", - /* 220 */ "expr ::= expr in_op LP exprlist RP", - /* 221 */ "expr ::= expr in_op LP select RP", - /* 222 */ "expr ::= expr in_op nm dbnm", - /* 223 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 224 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 225 */ "case_exprlist ::= WHEN expr THEN expr", - /* 226 */ "case_else ::= ELSE expr", - /* 227 */ "case_else ::=", - /* 228 */ "case_operand ::= expr", - /* 229 */ "case_operand ::=", - /* 230 */ "exprlist ::= exprlist COMMA expritem", - /* 231 */ "exprlist ::= expritem", - /* 232 */ "expritem ::= expr", - /* 233 */ "expritem ::=", - /* 234 */ "cmd ::= CREATE uniqueflag INDEX nm dbnm ON fullname LP idxlist RP onconf", - /* 235 */ "uniqueflag ::= UNIQUE", - /* 236 */ "uniqueflag ::=", - /* 237 */ "idxlist_opt ::=", - /* 238 */ "idxlist_opt ::= LP idxlist RP", - /* 239 */ "idxlist ::= idxlist COMMA idxitem collate sortorder", - /* 240 */ "idxlist ::= idxitem collate sortorder", - /* 241 */ "idxitem ::= nm", - /* 242 */ "cmd ::= DROP INDEX fullname", - /* 243 */ "cmd ::= VACUUM", - /* 244 */ "cmd ::= VACUUM nm", - /* 245 */ "cmd ::= PRAGMA nm dbnm EQ nm", - /* 246 */ "cmd ::= PRAGMA nm dbnm EQ ON", - /* 247 */ "cmd ::= PRAGMA nm dbnm EQ plus_num", - /* 248 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 249 */ "cmd ::= PRAGMA nm dbnm LP nm RP", - /* 250 */ "cmd ::= PRAGMA nm dbnm", - /* 251 */ "plus_num ::= plus_opt number", - /* 252 */ "minus_num ::= MINUS number", - /* 253 */ "number ::= INTEGER", - /* 254 */ "number ::= FLOAT", - /* 255 */ "plus_opt ::= PLUS", - /* 256 */ "plus_opt ::=", - /* 257 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END", - /* 258 */ "trigger_decl ::= temp TRIGGER nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 259 */ "trigger_time ::= BEFORE", - /* 260 */ "trigger_time ::= AFTER", - /* 261 */ "trigger_time ::= INSTEAD OF", - /* 262 */ "trigger_time ::=", - /* 263 */ "trigger_event ::= DELETE", - /* 264 */ "trigger_event ::= INSERT", - /* 265 */ "trigger_event ::= UPDATE", - /* 266 */ "trigger_event ::= UPDATE OF inscollist", - /* 267 */ "foreach_clause ::=", - /* 268 */ "foreach_clause ::= FOR EACH ROW", - /* 269 */ "foreach_clause ::= FOR EACH STATEMENT", - /* 270 */ "when_clause ::=", - /* 271 */ "when_clause ::= WHEN expr", - /* 272 */ "trigger_cmd_list ::= trigger_cmd SEMI trigger_cmd_list", - /* 273 */ "trigger_cmd_list ::=", - /* 274 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt", - /* 275 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP", - /* 276 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select", - /* 277 */ "trigger_cmd ::= DELETE FROM nm where_opt", - /* 278 */ "trigger_cmd ::= select", - /* 279 */ "expr ::= RAISE LP IGNORE RP", - /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 281 */ "raisetype ::= ROLLBACK", - /* 282 */ "raisetype ::= ABORT", - /* 283 */ "raisetype ::= FAIL", - /* 284 */ "cmd ::= DROP TRIGGER fullname", - /* 285 */ "cmd ::= ATTACH database_kw_opt ids AS nm key_opt", - /* 286 */ "key_opt ::=", - /* 287 */ "key_opt ::= KEY ids", - /* 288 */ "key_opt ::= KEY BLOB", - /* 289 */ "database_kw_opt ::= DATABASE", - /* 290 */ "database_kw_opt ::=", - /* 291 */ "cmd ::= DETACH database_kw_opt nm", -}; -#endif /* NDEBUG */ - -/* -** This function returns the symbolic name associated with a token -** value. -*/ -const char *sqlite3ParserTokenName(int tokenType){ -#ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ - return yyTokenName[tokenType]; - }else{ - return "Unknown"; - } -#else - return ""; -#endif -} - -/* -** This function allocates a new parser. -** The only argument is a pointer to a function which works like -** malloc. -** -** Inputs: -** A pointer to the function used to allocate memory. -** -** Outputs: -** A pointer to a parser. This pointer is used in subsequent calls -** to sqlite3Parser and sqlite3ParserFree. -*/ -void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){ - yyParser *pParser; - pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); - if( pParser ){ - pParser->yyidx = -1; - } - return pParser; -} - -/* The following function deletes the value associated with a -** symbol. The symbol can be either a terminal or nonterminal. -** "yymajor" is the symbol code, and "yypminor" is a pointer to -** the value. -*/ -static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ - switch( yymajor ){ - /* Here is inserted the actions which take place when a - ** terminal or non-terminal is destroyed. This can happen - ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is - ** being destroyed before it is finished parsing. - ** - ** Note: during a reduce, the only symbols destroyed are those - ** which appear on the RHS of the rule, but which are not used - ** inside the C code. - */ - case 146: - case 176: - case 193: -#line 303 "parse.y" -{sqlite3SelectDelete((yypminor->yy107));} -#line 1236 "parse.c" - break; - case 161: - case 181: - case 183: - case 191: - case 197: - case 210: -#line 552 "parse.y" -{sqlite3ExprDelete((yypminor->yy258));} -#line 1246 "parse.c" - break; - case 162: - case 170: - case 179: - case 182: - case 184: - case 186: - case 196: - case 199: - case 200: - case 203: - case 208: -#line 744 "parse.y" -{sqlite3ExprListDelete((yypminor->yy210));} -#line 1261 "parse.c" - break; - case 175: - case 180: - case 188: - case 189: -#line 428 "parse.y" -{sqlite3SrcListDelete((yypminor->yy259));} -#line 1269 "parse.c" - break; - case 192: - case 195: - case 202: -#line 446 "parse.y" -{sqlite3IdListDelete((yypminor->yy272));} -#line 1276 "parse.c" - break; - case 216: - case 221: -#line 833 "parse.y" -{sqlite3DeleteTriggerStep((yypminor->yy91));} -#line 1282 "parse.c" - break; - case 218: -#line 817 "parse.y" -{sqlite3IdListDelete((yypminor->yy146).b);} -#line 1287 "parse.c" - break; - default: break; /* If no destructor action specified: do nothing */ - } -} - -/* -** Pop the parser's stack once. -** -** If there is a destructor routine associated with the token which -** is popped from the stack, then call it. -** -** Return the major token number for the symbol popped. -*/ -static int yy_pop_parser_stack(yyParser *pParser){ - YYCODETYPE yymajor; - yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; - - if( pParser->yyidx<0 ) return 0; -#ifndef NDEBUG - if( yyTraceFILE && pParser->yyidx>=0 ){ - fprintf(yyTraceFILE,"%sPopping %s\n", - yyTracePrompt, - yyTokenName[yytos->major]); - } -#endif - yymajor = yytos->major; - yy_destructor( yymajor, &yytos->minor); - pParser->yyidx--; - return yymajor; -} - -/* -** Deallocate and destroy a parser. Destructors are all called for -** all stack elements before shutting the parser down. -** -** Inputs: -** <ul> -** <li> A pointer to the parser. This should be a pointer -** obtained from sqlite3ParserAlloc. -** <li> A pointer to a function used to reclaim memory obtained -** from malloc. -** </ul> -*/ -void sqlite3ParserFree( - void *p, /* The parser to be deleted */ - void (*freeProc)(void*) /* Function used to reclaim memory */ -){ - yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; - while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); - (*freeProc)((void*)pParser); -} - -/* -** Find the appropriate action for a parser given the terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_shift_action( - yyParser *pParser, /* The parser */ - int iLookAhead /* The look-ahead token */ -){ - int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; - - /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ - i = yy_shift_ofst[stateno]; - if( i==YY_SHIFT_USE_DFLT ){ - return yy_default[stateno]; - } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } - i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ -#ifdef YYFALLBACK - int iFallback; /* Fallback token */ - if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) - && (iFallback = yyFallback[iLookAhead])!=0 ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", - yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); - } -#endif - return yy_find_shift_action(pParser, iFallback); - } -#endif - return yy_default[stateno]; - }else{ - return yy_action[i]; - } -} - -/* -** Find the appropriate action for a parser given the non-terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_reduce_action( - yyParser *pParser, /* The parser */ - int iLookAhead /* The look-ahead token */ -){ - int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; - - i = yy_reduce_ofst[stateno]; - if( i==YY_REDUCE_USE_DFLT ){ - return yy_default[stateno]; - } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } - i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ - return yy_default[stateno]; - }else{ - return yy_action[i]; - } -} - -/* -** Perform a shift action. -*/ -static void yy_shift( - yyParser *yypParser, /* The parser to be shifted */ - int yyNewState, /* The new state to shift in */ - int yyMajor, /* The major token to shift in */ - YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ -){ - yyStackEntry *yytos; - yypParser->yyidx++; - if( yypParser->yyidx>=YYSTACKDEPTH ){ - sqlite3ParserARG_FETCH; - yypParser->yyidx--; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will execute if the parser - ** stack every overflows */ - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */ - return; - } - yytos = &yypParser->yystack[yypParser->yyidx]; - yytos->stateno = yyNewState; - yytos->major = yyMajor; - yytos->minor = *yypMinor; -#ifndef NDEBUG - if( yyTraceFILE && yypParser->yyidx>0 ){ - int i; - fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); - fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); - for(i=1; i<=yypParser->yyidx; i++) - fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); - fprintf(yyTraceFILE,"\n"); - } -#endif -} - -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - unsigned char nrhs; /* Number of right-hand side symbols in the rule */ -} yyRuleInfo[] = { - { 131, 1 }, - { 132, 2 }, - { 132, 1 }, - { 133, 3 }, - { 133, 1 }, - { 135, 1 }, - { 134, 1 }, - { 134, 0 }, - { 136, 3 }, - { 138, 0 }, - { 138, 1 }, - { 138, 2 }, - { 137, 0 }, - { 137, 1 }, - { 137, 1 }, - { 137, 1 }, - { 136, 2 }, - { 136, 2 }, - { 136, 2 }, - { 136, 2 }, - { 140, 5 }, - { 142, 1 }, - { 142, 0 }, - { 141, 4 }, - { 141, 2 }, - { 144, 3 }, - { 144, 1 }, - { 147, 3 }, - { 148, 1 }, - { 151, 1 }, - { 152, 1 }, - { 152, 1 }, - { 139, 1 }, - { 139, 1 }, - { 139, 1 }, - { 149, 0 }, - { 149, 1 }, - { 149, 4 }, - { 149, 6 }, - { 153, 1 }, - { 153, 2 }, - { 154, 1 }, - { 154, 1 }, - { 150, 2 }, - { 150, 0 }, - { 157, 3 }, - { 157, 1 }, - { 157, 2 }, - { 157, 2 }, - { 157, 2 }, - { 157, 2 }, - { 158, 2 }, - { 158, 3 }, - { 158, 4 }, - { 158, 2 }, - { 158, 5 }, - { 158, 4 }, - { 158, 1 }, - { 158, 2 }, - { 163, 0 }, - { 163, 2 }, - { 165, 2 }, - { 165, 3 }, - { 165, 3 }, - { 165, 3 }, - { 166, 2 }, - { 166, 2 }, - { 166, 1 }, - { 166, 1 }, - { 164, 3 }, - { 164, 2 }, - { 167, 0 }, - { 167, 2 }, - { 167, 2 }, - { 145, 0 }, - { 145, 2 }, - { 168, 3 }, - { 168, 2 }, - { 168, 1 }, - { 169, 2 }, - { 169, 6 }, - { 169, 5 }, - { 169, 3 }, - { 169, 10 }, - { 171, 0 }, - { 171, 1 }, - { 159, 0 }, - { 159, 3 }, - { 172, 0 }, - { 172, 2 }, - { 173, 1 }, - { 173, 1 }, - { 173, 1 }, - { 136, 3 }, - { 136, 7 }, - { 136, 3 }, - { 136, 1 }, - { 146, 1 }, - { 146, 3 }, - { 177, 1 }, - { 177, 2 }, - { 177, 1 }, - { 177, 1 }, - { 176, 9 }, - { 178, 1 }, - { 178, 1 }, - { 178, 0 }, - { 186, 2 }, - { 186, 0 }, - { 179, 3 }, - { 179, 2 }, - { 179, 4 }, - { 187, 2 }, - { 187, 1 }, - { 187, 0 }, - { 180, 0 }, - { 180, 2 }, - { 189, 2 }, - { 189, 0 }, - { 188, 6 }, - { 188, 7 }, - { 193, 1 }, - { 193, 1 }, - { 143, 0 }, - { 143, 2 }, - { 175, 2 }, - { 190, 1 }, - { 190, 1 }, - { 190, 2 }, - { 190, 3 }, - { 190, 4 }, - { 191, 2 }, - { 191, 0 }, - { 192, 4 }, - { 192, 0 }, - { 184, 0 }, - { 184, 3 }, - { 196, 5 }, - { 196, 3 }, - { 197, 1 }, - { 160, 1 }, - { 160, 1 }, - { 160, 0 }, - { 198, 0 }, - { 198, 2 }, - { 182, 0 }, - { 182, 3 }, - { 183, 0 }, - { 183, 2 }, - { 185, 0 }, - { 185, 2 }, - { 185, 4 }, - { 185, 4 }, - { 136, 4 }, - { 181, 0 }, - { 181, 2 }, - { 136, 6 }, - { 200, 5 }, - { 200, 3 }, - { 136, 8 }, - { 136, 5 }, - { 201, 2 }, - { 201, 1 }, - { 203, 3 }, - { 203, 1 }, - { 202, 0 }, - { 202, 3 }, - { 195, 3 }, - { 195, 1 }, - { 161, 3 }, - { 161, 1 }, - { 161, 1 }, - { 161, 1 }, - { 161, 3 }, - { 161, 5 }, - { 161, 1 }, - { 161, 1 }, - { 161, 1 }, - { 161, 1 }, - { 161, 1 }, - { 161, 4 }, - { 161, 4 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 161, 3 }, - { 204, 1 }, - { 204, 1 }, - { 204, 2 }, - { 204, 2 }, - { 161, 3 }, - { 161, 2 }, - { 161, 3 }, - { 161, 2 }, - { 161, 3 }, - { 161, 4 }, - { 161, 2 }, - { 161, 2 }, - { 161, 2 }, - { 161, 2 }, - { 161, 3 }, - { 205, 1 }, - { 205, 2 }, - { 161, 5 }, - { 206, 1 }, - { 206, 2 }, - { 161, 5 }, - { 161, 5 }, - { 161, 4 }, - { 161, 5 }, - { 208, 5 }, - { 208, 4 }, - { 209, 2 }, - { 209, 0 }, - { 207, 1 }, - { 207, 0 }, - { 199, 3 }, - { 199, 1 }, - { 210, 1 }, - { 210, 0 }, - { 136, 11 }, - { 211, 1 }, - { 211, 0 }, - { 162, 0 }, - { 162, 3 }, - { 170, 5 }, - { 170, 3 }, - { 212, 1 }, - { 136, 3 }, - { 136, 1 }, - { 136, 2 }, - { 136, 5 }, - { 136, 5 }, - { 136, 5 }, - { 136, 5 }, - { 136, 6 }, - { 136, 3 }, - { 155, 2 }, - { 156, 2 }, - { 214, 1 }, - { 214, 1 }, - { 213, 1 }, - { 213, 0 }, - { 136, 5 }, - { 215, 10 }, - { 217, 1 }, - { 217, 1 }, - { 217, 2 }, - { 217, 0 }, - { 218, 1 }, - { 218, 1 }, - { 218, 1 }, - { 218, 3 }, - { 219, 0 }, - { 219, 3 }, - { 219, 3 }, - { 220, 0 }, - { 220, 2 }, - { 216, 3 }, - { 216, 0 }, - { 221, 6 }, - { 221, 8 }, - { 221, 5 }, - { 221, 4 }, - { 221, 1 }, - { 161, 4 }, - { 161, 6 }, - { 174, 1 }, - { 174, 1 }, - { 174, 1 }, - { 136, 3 }, - { 136, 6 }, - { 223, 0 }, - { 223, 2 }, - { 223, 2 }, - { 222, 1 }, - { 222, 0 }, - { 136, 3 }, -}; - -static void yy_accept(yyParser*); /* Forward Declaration */ - -/* -** Perform a reduce action and the shift that must immediately -** follow the reduce. -*/ -static void yy_reduce( - yyParser *yypParser, /* The parser */ - int yyruleno /* Number of the rule by which to reduce */ -){ - int yygoto; /* The next state */ - int yyact; /* The next action */ - YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ - yyStackEntry *yymsp; /* The top of the parser's stack */ - int yysize; /* Amount to pop the stack */ - sqlite3ParserARG_FETCH; - yymsp = &yypParser->yystack[yypParser->yyidx]; -#ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ - fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, - yyRuleName[yyruleno]); - } -#endif /* NDEBUG */ - - switch( yyruleno ){ - /* Beginning here are the reduction cases. A typical example - ** follows: - ** case 0: - ** #line <lineno> <grammarfile> - ** { ... } // User supplied code - ** #line <lineno> <thisfile> - ** break; - */ - case 5: -#line 86 "parse.y" -{ sqlite3FinishCoding(pParse); } -#line 1794 "parse.c" - break; - case 6: -#line 87 "parse.y" -{ sqlite3BeginParse(pParse, 1); } -#line 1799 "parse.c" - break; - case 7: -#line 88 "parse.y" -{ sqlite3BeginParse(pParse, 0); } -#line 1804 "parse.c" - break; - case 8: -#line 93 "parse.y" -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy284);} -#line 1809 "parse.c" - break; - case 12: -#line 98 "parse.y" -{yygotominor.yy284 = TK_DEFERRED;} -#line 1814 "parse.c" - break; - case 13: - case 14: - case 15: - case 99: - case 101: - case 102: -#line 99 "parse.y" -{yygotominor.yy284 = yymsp[0].major;} -#line 1824 "parse.c" - break; - case 16: - case 17: -#line 102 "parse.y" -{sqlite3CommitTransaction(pParse);} -#line 1830 "parse.c" - break; - case 18: -#line 104 "parse.y" -{sqlite3RollbackTransaction(pParse);} -#line 1835 "parse.c" - break; - case 20: -#line 109 "parse.y" -{ - sqlite3StartTable(pParse,&yymsp[-4].minor.yy0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98,yymsp[-3].minor.yy284,0); -} -#line 1842 "parse.c" - break; - case 21: - case 72: - case 104: - case 216: - case 219: -#line 113 "parse.y" -{yygotominor.yy284 = 1;} -#line 1851 "parse.c" - break; - case 22: - case 71: - case 73: - case 84: - case 105: - case 106: - case 215: - case 218: -#line 114 "parse.y" -{yygotominor.yy284 = 0;} -#line 1863 "parse.c" - break; - case 23: -#line 115 "parse.y" -{ - sqlite3EndTable(pParse,&yymsp[0].minor.yy0,0); -} -#line 1870 "parse.c" - break; - case 24: -#line 118 "parse.y" -{ - sqlite3EndTable(pParse,0,yymsp[0].minor.yy107); - sqlite3SelectDelete(yymsp[0].minor.yy107); -} -#line 1878 "parse.c" - break; - case 28: -#line 130 "parse.y" -{sqlite3AddColumn(pParse,&yymsp[0].minor.yy98);} -#line 1883 "parse.c" - break; - case 29: - case 30: - case 31: - case 32: - case 33: - case 34: - case 253: - case 254: -#line 136 "parse.y" -{yygotominor.yy98 = yymsp[0].minor.yy0;} -#line 1895 "parse.c" - break; - case 36: -#line 185 "parse.y" -{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy98,&yymsp[0].minor.yy98);} -#line 1900 "parse.c" - break; - case 37: -#line 186 "parse.y" -{sqlite3AddColumnType(pParse,&yymsp[-3].minor.yy98,&yymsp[0].minor.yy0);} -#line 1905 "parse.c" - break; - case 38: -#line 188 "parse.y" -{sqlite3AddColumnType(pParse,&yymsp[-5].minor.yy98,&yymsp[0].minor.yy0);} -#line 1910 "parse.c" - break; - case 39: - case 112: - case 113: - case 124: - case 144: - case 241: - case 251: - case 252: -#line 190 "parse.y" -{yygotominor.yy98 = yymsp[0].minor.yy98;} -#line 1922 "parse.c" - break; - case 40: -#line 191 "parse.y" -{yygotominor.yy98.z=yymsp[-1].minor.yy98.z; yygotominor.yy98.n=yymsp[0].minor.yy98.n+(yymsp[0].minor.yy98.z-yymsp[-1].minor.yy98.z);} -#line 1927 "parse.c" - break; - case 41: -#line 193 "parse.y" -{ yygotominor.yy284 = atoi(yymsp[0].minor.yy98.z); } -#line 1932 "parse.c" - break; - case 42: -#line 194 "parse.y" -{ yygotominor.yy284 = -atoi(yymsp[0].minor.yy98.z); } -#line 1937 "parse.c" - break; - case 47: - case 48: -#line 199 "parse.y" -{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy98,0);} -#line 1943 "parse.c" - break; - case 49: -#line 201 "parse.y" -{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy98,1);} -#line 1948 "parse.c" - break; - case 52: -#line 208 "parse.y" -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy284);} -#line 1953 "parse.c" - break; - case 53: -#line 209 "parse.y" -{sqlite3AddPrimaryKey(pParse,0,yymsp[0].minor.yy284);} -#line 1958 "parse.c" - break; - case 54: -#line 210 "parse.y" -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy284,0,0);} -#line 1963 "parse.c" - break; - case 56: -#line 213 "parse.y" -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy98,yymsp[-1].minor.yy210,yymsp[0].minor.yy284);} -#line 1968 "parse.c" - break; - case 57: -#line 214 "parse.y" -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy284);} -#line 1973 "parse.c" - break; - case 58: -#line 215 "parse.y" -{sqlite3AddCollateType(pParse, yymsp[0].minor.yy98.z, yymsp[0].minor.yy98.n);} -#line 1978 "parse.c" - break; - case 59: -#line 223 "parse.y" -{ yygotominor.yy284 = OE_Restrict * 0x010101; } -#line 1983 "parse.c" - break; - case 60: -#line 224 "parse.y" -{ yygotominor.yy284 = (yymsp[-1].minor.yy284 & yymsp[0].minor.yy47.mask) | yymsp[0].minor.yy47.value; } -#line 1988 "parse.c" - break; - case 61: -#line 226 "parse.y" -{ yygotominor.yy47.value = 0; yygotominor.yy47.mask = 0x000000; } -#line 1993 "parse.c" - break; - case 62: -#line 227 "parse.y" -{ yygotominor.yy47.value = yymsp[0].minor.yy284; yygotominor.yy47.mask = 0x0000ff; } -#line 1998 "parse.c" - break; - case 63: -#line 228 "parse.y" -{ yygotominor.yy47.value = yymsp[0].minor.yy284<<8; yygotominor.yy47.mask = 0x00ff00; } -#line 2003 "parse.c" - break; - case 64: -#line 229 "parse.y" -{ yygotominor.yy47.value = yymsp[0].minor.yy284<<16; yygotominor.yy47.mask = 0xff0000; } -#line 2008 "parse.c" - break; - case 65: -#line 231 "parse.y" -{ yygotominor.yy284 = OE_SetNull; } -#line 2013 "parse.c" - break; - case 66: -#line 232 "parse.y" -{ yygotominor.yy284 = OE_SetDflt; } -#line 2018 "parse.c" - break; - case 67: -#line 233 "parse.y" -{ yygotominor.yy284 = OE_Cascade; } -#line 2023 "parse.c" - break; - case 68: -#line 234 "parse.y" -{ yygotominor.yy284 = OE_Restrict; } -#line 2028 "parse.c" - break; - case 69: - case 70: - case 85: - case 87: - case 89: - case 90: - case 161: -#line 236 "parse.y" -{yygotominor.yy284 = yymsp[0].minor.yy284;} -#line 2039 "parse.c" - break; - case 80: -#line 253 "parse.y" -{sqlite3AddPrimaryKey(pParse,yymsp[-2].minor.yy210,yymsp[0].minor.yy284);} -#line 2044 "parse.c" - break; - case 81: -#line 255 "parse.y" -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy210,yymsp[0].minor.yy284,0,0);} -#line 2049 "parse.c" - break; - case 83: -#line 258 "parse.y" -{ - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy210, &yymsp[-3].minor.yy98, yymsp[-2].minor.yy210, yymsp[-1].minor.yy284); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy284); -} -#line 2057 "parse.c" - break; - case 86: - case 88: -#line 272 "parse.y" -{yygotominor.yy284 = OE_Default;} -#line 2063 "parse.c" - break; - case 91: -#line 277 "parse.y" -{yygotominor.yy284 = OE_Ignore;} -#line 2068 "parse.c" - break; - case 92: - case 162: -#line 278 "parse.y" -{yygotominor.yy284 = OE_Replace;} -#line 2074 "parse.c" - break; - case 93: -#line 282 "parse.y" -{ - sqlite3DropTable(pParse, yymsp[0].minor.yy259, 0); -} -#line 2081 "parse.c" - break; - case 94: -#line 288 "parse.y" -{ - sqlite3CreateView(pParse, &yymsp[-6].minor.yy0, &yymsp[-3].minor.yy98, &yymsp[-2].minor.yy98, yymsp[0].minor.yy107, yymsp[-5].minor.yy284); -} -#line 2088 "parse.c" - break; - case 95: -#line 291 "parse.y" -{ - sqlite3DropTable(pParse, yymsp[0].minor.yy259, 1); -} -#line 2095 "parse.c" - break; - case 96: -#line 297 "parse.y" -{ - sqlite3Select(pParse, yymsp[0].minor.yy107, SRT_Callback, 0, 0, 0, 0, 0); - sqlite3SelectDelete(yymsp[0].minor.yy107); -} -#line 2103 "parse.c" - break; - case 97: - case 121: -#line 307 "parse.y" -{yygotominor.yy107 = yymsp[0].minor.yy107;} -#line 2109 "parse.c" - break; - case 98: -#line 308 "parse.y" -{ - if( yymsp[0].minor.yy107 ){ - yymsp[0].minor.yy107->op = yymsp[-1].minor.yy284; - yymsp[0].minor.yy107->pPrior = yymsp[-2].minor.yy107; - } - yygotominor.yy107 = yymsp[0].minor.yy107; -} -#line 2120 "parse.c" - break; - case 100: -#line 317 "parse.y" -{yygotominor.yy284 = TK_ALL;} -#line 2125 "parse.c" - break; - case 103: -#line 321 "parse.y" -{ - yygotominor.yy107 = sqlite3SelectNew(yymsp[-6].minor.yy210,yymsp[-5].minor.yy259,yymsp[-4].minor.yy258,yymsp[-3].minor.yy210,yymsp[-2].minor.yy258,yymsp[-1].minor.yy210,yymsp[-7].minor.yy284,yymsp[0].minor.yy404.limit,yymsp[0].minor.yy404.offset); -} -#line 2132 "parse.c" - break; - case 107: - case 238: -#line 342 "parse.y" -{yygotominor.yy210 = yymsp[-1].minor.yy210;} -#line 2138 "parse.c" - break; - case 108: - case 135: - case 145: - case 237: -#line 343 "parse.y" -{yygotominor.yy210 = 0;} -#line 2146 "parse.c" - break; - case 109: -#line 344 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-2].minor.yy210,yymsp[-1].minor.yy258,yymsp[0].minor.yy98.n?&yymsp[0].minor.yy98:0); -} -#line 2153 "parse.c" - break; - case 110: -#line 347 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-1].minor.yy210, sqlite3Expr(TK_ALL, 0, 0, 0), 0); -} -#line 2160 "parse.c" - break; - case 111: -#line 350 "parse.y" -{ - Expr *pRight = sqlite3Expr(TK_ALL, 0, 0, 0); - Expr *pLeft = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98); - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-3].minor.yy210, sqlite3Expr(TK_DOT, pLeft, pRight, 0), 0); -} -#line 2169 "parse.c" - break; - case 114: -#line 362 "parse.y" -{yygotominor.yy98.n = 0;} -#line 2174 "parse.c" - break; - case 115: -#line 374 "parse.y" -{yygotominor.yy259 = sqliteMalloc(sizeof(*yygotominor.yy259));} -#line 2179 "parse.c" - break; - case 116: -#line 375 "parse.y" -{yygotominor.yy259 = yymsp[0].minor.yy259;} -#line 2184 "parse.c" - break; - case 117: -#line 380 "parse.y" -{ - yygotominor.yy259 = yymsp[-1].minor.yy259; - if( yygotominor.yy259 && yygotominor.yy259->nSrc>0 ) yygotominor.yy259->a[yygotominor.yy259->nSrc-1].jointype = yymsp[0].minor.yy284; -} -#line 2192 "parse.c" - break; - case 118: -#line 384 "parse.y" -{yygotominor.yy259 = 0;} -#line 2197 "parse.c" - break; - case 119: -#line 385 "parse.y" -{ - yygotominor.yy259 = sqlite3SrcListAppend(yymsp[-5].minor.yy259,&yymsp[-4].minor.yy98,&yymsp[-3].minor.yy98); - if( yymsp[-2].minor.yy98.n ) sqlite3SrcListAddAlias(yygotominor.yy259,&yymsp[-2].minor.yy98); - if( yymsp[-1].minor.yy258 ){ - if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pOn = yymsp[-1].minor.yy258; } - else { sqlite3ExprDelete(yymsp[-1].minor.yy258); } - } - if( yymsp[0].minor.yy272 ){ - if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pUsing = yymsp[0].minor.yy272; } - else { sqlite3IdListDelete(yymsp[0].minor.yy272); } - } -} -#line 2213 "parse.c" - break; - case 120: -#line 398 "parse.y" -{ - yygotominor.yy259 = sqlite3SrcListAppend(yymsp[-6].minor.yy259,0,0); - yygotominor.yy259->a[yygotominor.yy259->nSrc-1].pSelect = yymsp[-4].minor.yy107; - if( yymsp[-2].minor.yy98.n ) sqlite3SrcListAddAlias(yygotominor.yy259,&yymsp[-2].minor.yy98); - if( yymsp[-1].minor.yy258 ){ - if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pOn = yymsp[-1].minor.yy258; } - else { sqlite3ExprDelete(yymsp[-1].minor.yy258); } - } - if( yymsp[0].minor.yy272 ){ - if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pUsing = yymsp[0].minor.yy272; } - else { sqlite3IdListDelete(yymsp[0].minor.yy272); } - } -} -#line 2230 "parse.c" - break; - case 122: -#line 419 "parse.y" -{ - yygotominor.yy107 = sqlite3SelectNew(0,yymsp[0].minor.yy259,0,0,0,0,0,-1,0); -} -#line 2237 "parse.c" - break; - case 123: -#line 424 "parse.y" -{yygotominor.yy98.z=0; yygotominor.yy98.n=0;} -#line 2242 "parse.c" - break; - case 125: -#line 429 "parse.y" -{yygotominor.yy259 = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98);} -#line 2247 "parse.c" - break; - case 126: - case 127: -#line 433 "parse.y" -{ yygotominor.yy284 = JT_INNER; } -#line 2253 "parse.c" - break; - case 128: -#line 435 "parse.y" -{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } -#line 2258 "parse.c" - break; - case 129: -#line 436 "parse.y" -{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy98,0); } -#line 2263 "parse.c" - break; - case 130: -#line 438 "parse.y" -{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy98,&yymsp[-1].minor.yy98); } -#line 2268 "parse.c" - break; - case 131: - case 139: - case 148: - case 155: - case 226: - case 228: - case 232: -#line 442 "parse.y" -{yygotominor.yy258 = yymsp[0].minor.yy258;} -#line 2279 "parse.c" - break; - case 132: - case 147: - case 154: - case 227: - case 229: - case 233: -#line 443 "parse.y" -{yygotominor.yy258 = 0;} -#line 2289 "parse.c" - break; - case 133: - case 166: -#line 447 "parse.y" -{yygotominor.yy272 = yymsp[-1].minor.yy272;} -#line 2295 "parse.c" - break; - case 134: - case 165: -#line 448 "parse.y" -{yygotominor.yy272 = 0;} -#line 2301 "parse.c" - break; - case 136: - case 146: -#line 459 "parse.y" -{yygotominor.yy210 = yymsp[0].minor.yy210;} -#line 2307 "parse.c" - break; - case 137: -#line 460 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210,yymsp[-2].minor.yy258,yymsp[-1].minor.yy98.n>0?&yymsp[-1].minor.yy98:0); - if( yygotominor.yy210 ) yygotominor.yy210->a[yygotominor.yy210->nExpr-1].sortOrder = yymsp[0].minor.yy284; -} -#line 2315 "parse.c" - break; - case 138: -#line 464 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[-2].minor.yy258,yymsp[-1].minor.yy98.n>0?&yymsp[-1].minor.yy98:0); - if( yygotominor.yy210 && yygotominor.yy210->a ) yygotominor.yy210->a[0].sortOrder = yymsp[0].minor.yy284; -} -#line 2323 "parse.c" - break; - case 140: - case 142: -#line 473 "parse.y" -{yygotominor.yy284 = SQLITE_SO_ASC;} -#line 2329 "parse.c" - break; - case 141: -#line 474 "parse.y" -{yygotominor.yy284 = SQLITE_SO_DESC;} -#line 2334 "parse.c" - break; - case 143: -#line 476 "parse.y" -{yygotominor.yy98.z = 0; yygotominor.yy98.n = 0;} -#line 2339 "parse.c" - break; - case 149: -#line 490 "parse.y" -{yygotominor.yy404.limit = -1; yygotominor.yy404.offset = 0;} -#line 2344 "parse.c" - break; - case 150: -#line 491 "parse.y" -{yygotominor.yy404.limit = yymsp[0].minor.yy284; yygotominor.yy404.offset = 0;} -#line 2349 "parse.c" - break; - case 151: -#line 493 "parse.y" -{yygotominor.yy404.limit = yymsp[-2].minor.yy284; yygotominor.yy404.offset = yymsp[0].minor.yy284;} -#line 2354 "parse.c" - break; - case 152: -#line 495 "parse.y" -{yygotominor.yy404.limit = yymsp[0].minor.yy284; yygotominor.yy404.offset = yymsp[-2].minor.yy284;} -#line 2359 "parse.c" - break; - case 153: -#line 499 "parse.y" -{sqlite3DeleteFrom(pParse,yymsp[-1].minor.yy259,yymsp[0].minor.yy258);} -#line 2364 "parse.c" - break; - case 156: -#line 513 "parse.y" -{sqlite3Update(pParse,yymsp[-3].minor.yy259,yymsp[-1].minor.yy210,yymsp[0].minor.yy258,yymsp[-4].minor.yy284);} -#line 2369 "parse.c" - break; - case 157: -#line 516 "parse.y" -{yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210,yymsp[0].minor.yy258,&yymsp[-2].minor.yy98);} -#line 2374 "parse.c" - break; - case 158: -#line 517 "parse.y" -{yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[0].minor.yy258,&yymsp[-2].minor.yy98);} -#line 2379 "parse.c" - break; - case 159: -#line 523 "parse.y" -{sqlite3Insert(pParse, yymsp[-5].minor.yy259, yymsp[-1].minor.yy210, 0, yymsp[-4].minor.yy272, yymsp[-7].minor.yy284);} -#line 2384 "parse.c" - break; - case 160: -#line 525 "parse.y" -{sqlite3Insert(pParse, yymsp[-2].minor.yy259, 0, yymsp[0].minor.yy107, yymsp[-1].minor.yy272, yymsp[-4].minor.yy284);} -#line 2389 "parse.c" - break; - case 163: - case 230: -#line 535 "parse.y" -{yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-2].minor.yy210,yymsp[0].minor.yy258,0);} -#line 2395 "parse.c" - break; - case 164: - case 231: -#line 536 "parse.y" -{yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[0].minor.yy258,0);} -#line 2401 "parse.c" - break; - case 167: -#line 545 "parse.y" -{yygotominor.yy272 = sqlite3IdListAppend(yymsp[-2].minor.yy272,&yymsp[0].minor.yy98);} -#line 2406 "parse.c" - break; - case 168: -#line 546 "parse.y" -{yygotominor.yy272 = sqlite3IdListAppend(0,&yymsp[0].minor.yy98);} -#line 2411 "parse.c" - break; - case 169: -#line 554 "parse.y" -{yygotominor.yy258 = yymsp[-1].minor.yy258; sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); } -#line 2416 "parse.c" - break; - case 170: - case 175: - case 176: - case 177: - case 178: -#line 555 "parse.y" -{yygotominor.yy258 = sqlite3Expr(yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);} -#line 2425 "parse.c" - break; - case 171: - case 172: -#line 556 "parse.y" -{yygotominor.yy258 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy0);} -#line 2431 "parse.c" - break; - case 173: -#line 558 "parse.y" -{ - Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98); - Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy98); - yygotominor.yy258 = sqlite3Expr(TK_DOT, temp1, temp2, 0); -} -#line 2440 "parse.c" - break; - case 174: -#line 563 "parse.y" -{ - Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-4].minor.yy98); - Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98); - Expr *temp3 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy98); - Expr *temp4 = sqlite3Expr(TK_DOT, temp2, temp3, 0); - yygotominor.yy258 = sqlite3Expr(TK_DOT, temp1, temp4, 0); -} -#line 2451 "parse.c" - break; - case 179: -#line 574 "parse.y" -{ - Token *pToken = &yymsp[0].minor.yy0; - Expr *pExpr = yygotominor.yy258 = sqlite3Expr(TK_VARIABLE, 0, 0, pToken); - sqlite3ExprAssignVarNumber(pParse, pExpr); -} -#line 2460 "parse.c" - break; - case 180: -#line 579 "parse.y" -{ - yygotominor.yy258 = sqlite3ExprFunction(yymsp[-1].minor.yy210, &yymsp[-3].minor.yy0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); -} -#line 2468 "parse.c" - break; - case 181: -#line 583 "parse.y" -{ - yygotominor.yy258 = sqlite3ExprFunction(0, &yymsp[-3].minor.yy0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); -} -#line 2476 "parse.c" - break; - case 182: - case 183: - case 184: - case 185: - case 186: - case 187: - case 188: - case 189: - case 190: - case 191: - case 192: - case 193: - case 194: - case 195: - case 196: - case 197: - case 198: - case 199: -#line 587 "parse.y" -{yygotominor.yy258 = sqlite3Expr(yymsp[-1].major, yymsp[-2].minor.yy258, yymsp[0].minor.yy258, 0);} -#line 2498 "parse.c" - break; - case 200: -#line 606 "parse.y" -{yygotominor.yy342.opcode = TK_LIKE; yygotominor.yy342.not = 0;} -#line 2503 "parse.c" - break; - case 201: -#line 607 "parse.y" -{yygotominor.yy342.opcode = TK_GLOB; yygotominor.yy342.not = 0;} -#line 2508 "parse.c" - break; - case 202: -#line 608 "parse.y" -{yygotominor.yy342.opcode = TK_LIKE; yygotominor.yy342.not = 1;} -#line 2513 "parse.c" - break; - case 203: -#line 609 "parse.y" -{yygotominor.yy342.opcode = TK_GLOB; yygotominor.yy342.not = 1;} -#line 2518 "parse.c" - break; - case 204: -#line 610 "parse.y" -{ - ExprList *pList = sqlite3ExprListAppend(0, yymsp[0].minor.yy258, 0); - pList = sqlite3ExprListAppend(pList, yymsp[-2].minor.yy258, 0); - yygotominor.yy258 = sqlite3ExprFunction(pList, 0); - if( yygotominor.yy258 ) yygotominor.yy258->op = yymsp[-1].minor.yy342.opcode; - if( yymsp[-1].minor.yy342.not ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258, &yymsp[-2].minor.yy258->span, &yymsp[0].minor.yy258->span); -} -#line 2530 "parse.c" - break; - case 205: -#line 618 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_ISNULL, yymsp[-1].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2538 "parse.c" - break; - case 206: -#line 622 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_ISNULL, yymsp[-2].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2546 "parse.c" - break; - case 207: -#line 626 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-1].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2554 "parse.c" - break; - case 208: -#line 630 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-2].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2562 "parse.c" - break; - case 209: -#line 634 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-3].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2570 "parse.c" - break; - case 210: - case 211: -#line 638 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(yymsp[-1].major, yymsp[0].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span); -} -#line 2579 "parse.c" - break; - case 212: -#line 646 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_UMINUS, yymsp[0].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span); -} -#line 2587 "parse.c" - break; - case 213: -#line 650 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_UPLUS, yymsp[0].minor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span); -} -#line 2595 "parse.c" - break; - case 214: -#line 654 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_SELECT, 0, 0, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pSelect = yymsp[-1].minor.yy107; - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); -} -#line 2604 "parse.c" - break; - case 217: -#line 662 "parse.y" -{ - ExprList *pList = sqlite3ExprListAppend(0, yymsp[-2].minor.yy258, 0); - pList = sqlite3ExprListAppend(pList, yymsp[0].minor.yy258, 0); - yygotominor.yy258 = sqlite3Expr(TK_BETWEEN, yymsp[-4].minor.yy258, 0, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pList = pList; - if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy258->span); -} -#line 2616 "parse.c" - break; - case 220: -#line 673 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy258, 0, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pList = yymsp[-1].minor.yy210; - if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2626 "parse.c" - break; - case 221: -#line 679 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy258, 0, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pSelect = yymsp[-1].minor.yy107; - if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy0); -} -#line 2636 "parse.c" - break; - case 222: -#line 685 "parse.y" -{ - SrcList *pSrc = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98); - yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-3].minor.yy258, 0, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,-1,0); - if( yymsp[-2].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0); - sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy258->span,yymsp[0].minor.yy98.z?&yymsp[0].minor.yy98:&yymsp[-1].minor.yy98); -} -#line 2647 "parse.c" - break; - case 223: -#line 695 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_CASE, yymsp[-3].minor.yy258, yymsp[-1].minor.yy258, 0); - if( yygotominor.yy258 ) yygotominor.yy258->pList = yymsp[-2].minor.yy210; - sqlite3ExprSpan(yygotominor.yy258, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0); -} -#line 2656 "parse.c" - break; - case 224: -#line 702 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210, yymsp[-2].minor.yy258, 0); - yygotominor.yy210 = sqlite3ExprListAppend(yygotominor.yy210, yymsp[0].minor.yy258, 0); -} -#line 2664 "parse.c" - break; - case 225: -#line 706 "parse.y" -{ - yygotominor.yy210 = sqlite3ExprListAppend(0, yymsp[-2].minor.yy258, 0); - yygotominor.yy210 = sqlite3ExprListAppend(yygotominor.yy210, yymsp[0].minor.yy258, 0); -} -#line 2672 "parse.c" - break; - case 234: -#line 731 "parse.y" -{ - if( yymsp[-9].minor.yy284!=OE_None ) yymsp[-9].minor.yy284 = yymsp[0].minor.yy284; - if( yymsp[-9].minor.yy284==OE_Default) yymsp[-9].minor.yy284 = OE_Abort; - sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy98, &yymsp[-6].minor.yy98, yymsp[-4].minor.yy259, yymsp[-2].minor.yy210, yymsp[-9].minor.yy284, &yymsp[-10].minor.yy0, &yymsp[-1].minor.yy0); -} -#line 2681 "parse.c" - break; - case 235: - case 282: -#line 738 "parse.y" -{yygotominor.yy284 = OE_Abort;} -#line 2687 "parse.c" - break; - case 236: -#line 739 "parse.y" -{yygotominor.yy284 = OE_None;} -#line 2692 "parse.c" - break; - case 239: -#line 749 "parse.y" -{ - Expr *p = 0; - if( yymsp[-1].minor.yy98.n>0 ){ - p = sqlite3Expr(TK_COLUMN, 0, 0, 0); - if( p ) p->pColl = sqlite3LocateCollSeq(pParse, yymsp[-1].minor.yy98.z, yymsp[-1].minor.yy98.n); - } - yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210, p, &yymsp[-2].minor.yy98); -} -#line 2704 "parse.c" - break; - case 240: -#line 757 "parse.y" -{ - Expr *p = 0; - if( yymsp[-1].minor.yy98.n>0 ){ - p = sqlite3Expr(TK_COLUMN, 0, 0, 0); - if( p ) p->pColl = sqlite3LocateCollSeq(pParse, yymsp[-1].minor.yy98.z, yymsp[-1].minor.yy98.n); - } - yygotominor.yy210 = sqlite3ExprListAppend(0, p, &yymsp[-2].minor.yy98); -} -#line 2716 "parse.c" - break; - case 242: -#line 770 "parse.y" -{sqlite3DropIndex(pParse, yymsp[0].minor.yy259);} -#line 2721 "parse.c" - break; - case 243: - case 244: -#line 774 "parse.y" -{sqlite3Vacuum(pParse,0);} -#line 2727 "parse.c" - break; - case 245: - case 247: -#line 779 "parse.y" -{sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy98,0);} -#line 2733 "parse.c" - break; - case 246: -#line 780 "parse.y" -{sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy0,0);} -#line 2738 "parse.c" - break; - case 248: -#line 782 "parse.y" -{ - sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy98,1); -} -#line 2745 "parse.c" - break; - case 249: -#line 785 "parse.y" -{sqlite3Pragma(pParse,&yymsp[-4].minor.yy98,&yymsp[-3].minor.yy98,&yymsp[-1].minor.yy98,0);} -#line 2750 "parse.c" - break; - case 250: -#line 786 "parse.y" -{sqlite3Pragma(pParse,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98,0,0);} -#line 2755 "parse.c" - break; - case 257: -#line 796 "parse.y" -{ - Token all; - all.z = yymsp[-3].minor.yy98.z; - all.n = (yymsp[0].minor.yy0.z - yymsp[-3].minor.yy98.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy91, &all); -} -#line 2765 "parse.c" - break; - case 258: -#line 805 "parse.y" -{ - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy98, &yymsp[-6].minor.yy98, yymsp[-5].minor.yy284, yymsp[-4].minor.yy146.a, yymsp[-4].minor.yy146.b, yymsp[-2].minor.yy259, yymsp[-1].minor.yy284, yymsp[0].minor.yy258, yymsp[-9].minor.yy284); - yygotominor.yy98 = (yymsp[-6].minor.yy98.n==0?yymsp[-7].minor.yy98:yymsp[-6].minor.yy98); -} -#line 2773 "parse.c" - break; - case 259: - case 262: -#line 811 "parse.y" -{ yygotominor.yy284 = TK_BEFORE; } -#line 2779 "parse.c" - break; - case 260: -#line 812 "parse.y" -{ yygotominor.yy284 = TK_AFTER; } -#line 2784 "parse.c" - break; - case 261: -#line 813 "parse.y" -{ yygotominor.yy284 = TK_INSTEAD;} -#line 2789 "parse.c" - break; - case 263: - case 264: - case 265: -#line 818 "parse.y" -{yygotominor.yy146.a = yymsp[0].major; yygotominor.yy146.b = 0;} -#line 2796 "parse.c" - break; - case 266: -#line 821 "parse.y" -{yygotominor.yy146.a = TK_UPDATE; yygotominor.yy146.b = yymsp[0].minor.yy272;} -#line 2801 "parse.c" - break; - case 267: - case 268: -#line 824 "parse.y" -{ yygotominor.yy284 = TK_ROW; } -#line 2807 "parse.c" - break; - case 269: -#line 826 "parse.y" -{ yygotominor.yy284 = TK_STATEMENT; } -#line 2812 "parse.c" - break; - case 270: -#line 829 "parse.y" -{ yygotominor.yy258 = 0; } -#line 2817 "parse.c" - break; - case 271: -#line 830 "parse.y" -{ yygotominor.yy258 = yymsp[0].minor.yy258; } -#line 2822 "parse.c" - break; - case 272: -#line 834 "parse.y" -{ - yymsp[-2].minor.yy91->pNext = yymsp[0].minor.yy91; - yygotominor.yy91 = yymsp[-2].minor.yy91; -} -#line 2830 "parse.c" - break; - case 273: -#line 838 "parse.y" -{ yygotominor.yy91 = 0; } -#line 2835 "parse.c" - break; - case 274: -#line 844 "parse.y" -{ yygotominor.yy91 = sqlite3TriggerUpdateStep(&yymsp[-3].minor.yy98, yymsp[-1].minor.yy210, yymsp[0].minor.yy258, yymsp[-4].minor.yy284); } -#line 2840 "parse.c" - break; - case 275: -#line 849 "parse.y" -{yygotominor.yy91 = sqlite3TriggerInsertStep(&yymsp[-5].minor.yy98, yymsp[-4].minor.yy272, yymsp[-1].minor.yy210, 0, yymsp[-7].minor.yy284);} -#line 2845 "parse.c" - break; - case 276: -#line 852 "parse.y" -{yygotominor.yy91 = sqlite3TriggerInsertStep(&yymsp[-2].minor.yy98, yymsp[-1].minor.yy272, 0, yymsp[0].minor.yy107, yymsp[-4].minor.yy284);} -#line 2850 "parse.c" - break; - case 277: -#line 856 "parse.y" -{yygotominor.yy91 = sqlite3TriggerDeleteStep(&yymsp[-1].minor.yy98, yymsp[0].minor.yy258);} -#line 2855 "parse.c" - break; - case 278: -#line 859 "parse.y" -{yygotominor.yy91 = sqlite3TriggerSelectStep(yymsp[0].minor.yy107); } -#line 2860 "parse.c" - break; - case 279: -#line 862 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_RAISE, 0, 0, 0); - yygotominor.yy258->iColumn = OE_Ignore; - sqlite3ExprSpan(yygotominor.yy258, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0); -} -#line 2869 "parse.c" - break; - case 280: -#line 867 "parse.y" -{ - yygotominor.yy258 = sqlite3Expr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy98); - yygotominor.yy258->iColumn = yymsp[-3].minor.yy284; - sqlite3ExprSpan(yygotominor.yy258, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0); -} -#line 2878 "parse.c" - break; - case 281: -#line 873 "parse.y" -{yygotominor.yy284 = OE_Rollback;} -#line 2883 "parse.c" - break; - case 283: -#line 875 "parse.y" -{yygotominor.yy284 = OE_Fail;} -#line 2888 "parse.c" - break; - case 284: -#line 879 "parse.y" -{ - sqlite3DropTrigger(pParse,yymsp[0].minor.yy259); -} -#line 2895 "parse.c" - break; - case 285: -#line 884 "parse.y" -{ - sqlite3Attach(pParse, &yymsp[-3].minor.yy98, &yymsp[-1].minor.yy98, yymsp[0].minor.yy292.type, &yymsp[0].minor.yy292.key); -} -#line 2902 "parse.c" - break; - case 286: -#line 888 "parse.y" -{ yygotominor.yy292.type = 0; } -#line 2907 "parse.c" - break; - case 287: -#line 889 "parse.y" -{ yygotominor.yy292.type=1; yygotominor.yy292.key = yymsp[0].minor.yy98; } -#line 2912 "parse.c" - break; - case 288: -#line 890 "parse.y" -{ yygotominor.yy292.type=2; yygotominor.yy292.key = yymsp[0].minor.yy0; } -#line 2917 "parse.c" - break; - case 291: -#line 896 "parse.y" -{ - sqlite3Detach(pParse, &yymsp[0].minor.yy98); -} -#line 2924 "parse.c" - break; - }; - yygoto = yyRuleInfo[yyruleno].lhs; - yysize = yyRuleInfo[yyruleno].nrhs; - yypParser->yyidx -= yysize; - yyact = yy_find_reduce_action(yypParser,yygoto); - if( yyact < YYNSTATE ){ - yy_shift(yypParser,yyact,yygoto,&yygotominor); - }else if( yyact == YYNSTATE + YYNRULE + 1 ){ - yy_accept(yypParser); - } -} - -/* -** The following code executes when the parse fails -*/ -static void yy_parse_failed( - yyParser *yypParser /* The parser */ -){ - sqlite3ParserARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser fails */ - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* -** The following code executes when a syntax error first occurs. -*/ -static void yy_syntax_error( - yyParser *yypParser, /* The parser */ - int yymajor, /* The major type of the error token */ - YYMINORTYPE yyminor /* The minor type of the error token */ -){ - sqlite3ParserARG_FETCH; -#define TOKEN (yyminor.yy0) -#line 23 "parse.y" - - if( pParse->zErrMsg==0 ){ - if( TOKEN.z[0] ){ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); - }else{ - sqlite3ErrorMsg(pParse, "incomplete SQL statement"); - } - } -#line 2976 "parse.c" - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* -** The following is executed when the parser accepts -*/ -static void yy_accept( - yyParser *yypParser /* The parser */ -){ - sqlite3ParserARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser accepts */ - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* The main parser program. -** The first argument is a pointer to a structure obtained from -** "sqlite3ParserAlloc" which describes the current state of the parser. -** The second argument is the major token number. The third is -** the minor token. The fourth optional argument is whatever the -** user wants (and specified in the grammar) and is available for -** use by the action routines. -** -** Inputs: -** <ul> -** <li> A pointer to the parser (an opaque structure.) -** <li> The major token number. -** <li> The minor token number. -** <li> An option argument of a grammar-specified type. -** </ul> -** -** Outputs: -** None. -*/ -void sqlite3Parser( - void *yyp, /* The parser */ - int yymajor, /* The major token code number */ - sqlite3ParserTOKENTYPE yyminor /* The value for the token */ - sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */ -){ - YYMINORTYPE yyminorunion; - int yyact; /* The parser action. */ - int yyendofinput; /* True if we are at the end of input */ - int yyerrorhit = 0; /* True if yymajor has invoked an error */ - yyParser *yypParser; /* The parser */ - - /* (re)initialize the parser, if necessary */ - yypParser = (yyParser*)yyp; - if( yypParser->yyidx<0 ){ - if( yymajor==0 ) return; - yypParser->yyidx = 0; - yypParser->yyerrcnt = -1; - yypParser->yystack[0].stateno = 0; - yypParser->yystack[0].major = 0; - } - yyminorunion.yy0 = yyminor; - yyendofinput = (yymajor==0); - sqlite3ParserARG_STORE; - -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); - } -#endif - - do{ - yyact = yy_find_shift_action(yypParser,yymajor); - if( yyact<YYNSTATE ){ - yy_shift(yypParser,yyact,yymajor,&yyminorunion); - yypParser->yyerrcnt--; - if( yyendofinput && yypParser->yyidx>=0 ){ - yymajor = 0; - }else{ - yymajor = YYNOCODE; - } - }else if( yyact < YYNSTATE + YYNRULE ){ - yy_reduce(yypParser,yyact-YYNSTATE); - }else if( yyact == YY_ERROR_ACTION ){ - int yymx; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); - } -#endif -#ifdef YYERRORSYMBOL - /* A syntax error has occurred. - ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". - ** - ** This is what we do if the grammar does define ERROR: - ** - ** * Call the %syntax_error function. - ** - ** * Begin popping the stack until we enter a state where - ** it is legal to shift the error symbol, then shift - ** the error symbol. - ** - ** * Set the error count to three. - ** - ** * Begin accepting and shifting new tokens. No new error - ** processing will occur until three tokens have been - ** shifted successfully. - ** - */ - if( yypParser->yyerrcnt<0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yymx = yypParser->yystack[yypParser->yyidx].major; - if( yymx==YYERRORSYMBOL || yyerrorhit ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sDiscard input token %s\n", - yyTracePrompt,yyTokenName[yymajor]); - } -#endif - yy_destructor(yymajor,&yyminorunion); - yymajor = YYNOCODE; - }else{ - while( - yypParser->yyidx >= 0 && - yymx != YYERRORSYMBOL && - (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE - ){ - yy_pop_parser_stack(yypParser); - } - if( yypParser->yyidx < 0 || yymajor==0 ){ - yy_destructor(yymajor,&yyminorunion); - yy_parse_failed(yypParser); - yymajor = YYNOCODE; - }else if( yymx!=YYERRORSYMBOL ){ - YYMINORTYPE u2; - u2.YYERRSYMDT = 0; - yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); - } - } - yypParser->yyerrcnt = 3; - yyerrorhit = 1; -#else /* YYERRORSYMBOL is not defined */ - /* This is what we do if the grammar does not define ERROR: - ** - ** * Report an error message, and throw away the input token. - ** - ** * If the input token is $, then fail the parse. - ** - ** As before, subsequent error messages are suppressed until - ** three input tokens have been successfully shifted. - */ - if( yypParser->yyerrcnt<=0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yypParser->yyerrcnt = 3; - yy_destructor(yymajor,&yyminorunion); - if( yyendofinput ){ - yy_parse_failed(yypParser); - } - yymajor = YYNOCODE; -#endif - }else{ - yy_accept(yypParser); - yymajor = YYNOCODE; - } - }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); - return; -} diff --git a/kopete/plugins/statistics/sqlite/parse.h b/kopete/plugins/statistics/sqlite/parse.h deleted file mode 100644 index 547319ed..00000000 --- a/kopete/plugins/statistics/sqlite/parse.h +++ /dev/null @@ -1,129 +0,0 @@ -#define TK_END_OF_FILE 1 -#define TK_ILLEGAL 2 -#define TK_SPACE 3 -#define TK_UNCLOSED_STRING 4 -#define TK_COMMENT 5 -#define TK_FUNCTION 6 -#define TK_COLUMN 7 -#define TK_AGG_FUNCTION 8 -#define TK_SEMI 9 -#define TK_EXPLAIN 10 -#define TK_BEGIN 11 -#define TK_TRANSACTION 12 -#define TK_DEFERRED 13 -#define TK_IMMEDIATE 14 -#define TK_EXCLUSIVE 15 -#define TK_COMMIT 16 -#define TK_END 17 -#define TK_ROLLBACK 18 -#define TK_CREATE 19 -#define TK_TABLE 20 -#define TK_TEMP 21 -#define TK_LP 22 -#define TK_RP 23 -#define TK_AS 24 -#define TK_COMMA 25 -#define TK_ID 26 -#define TK_ABORT 27 -#define TK_AFTER 28 -#define TK_ASC 29 -#define TK_ATTACH 30 -#define TK_BEFORE 31 -#define TK_CASCADE 32 -#define TK_CONFLICT 33 -#define TK_DATABASE 34 -#define TK_DESC 35 -#define TK_DETACH 36 -#define TK_EACH 37 -#define TK_FAIL 38 -#define TK_FOR 39 -#define TK_GLOB 40 -#define TK_IGNORE 41 -#define TK_INITIALLY 42 -#define TK_INSTEAD 43 -#define TK_LIKE 44 -#define TK_MATCH 45 -#define TK_KEY 46 -#define TK_OF 47 -#define TK_OFFSET 48 -#define TK_PRAGMA 49 -#define TK_RAISE 50 -#define TK_REPLACE 51 -#define TK_RESTRICT 52 -#define TK_ROW 53 -#define TK_STATEMENT 54 -#define TK_TRIGGER 55 -#define TK_VACUUM 56 -#define TK_VIEW 57 -#define TK_OR 58 -#define TK_AND 59 -#define TK_NOT 60 -#define TK_IS 61 -#define TK_BETWEEN 62 -#define TK_IN 63 -#define TK_ISNULL 64 -#define TK_NOTNULL 65 -#define TK_NE 66 -#define TK_EQ 67 -#define TK_GT 68 -#define TK_LE 69 -#define TK_LT 70 -#define TK_GE 71 -#define TK_BITAND 72 -#define TK_BITOR 73 -#define TK_LSHIFT 74 -#define TK_RSHIFT 75 -#define TK_PLUS 76 -#define TK_MINUS 77 -#define TK_STAR 78 -#define TK_SLASH 79 -#define TK_REM 80 -#define TK_CONCAT 81 -#define TK_UMINUS 82 -#define TK_UPLUS 83 -#define TK_BITNOT 84 -#define TK_STRING 85 -#define TK_JOIN_KW 86 -#define TK_CONSTRAINT 87 -#define TK_DEFAULT 88 -#define TK_NULL 89 -#define TK_PRIMARY 90 -#define TK_UNIQUE 91 -#define TK_CHECK 92 -#define TK_REFERENCES 93 -#define TK_COLLATE 94 -#define TK_ON 95 -#define TK_DELETE 96 -#define TK_UPDATE 97 -#define TK_INSERT 98 -#define TK_SET 99 -#define TK_DEFERRABLE 100 -#define TK_FOREIGN 101 -#define TK_DROP 102 -#define TK_UNION 103 -#define TK_ALL 104 -#define TK_INTERSECT 105 -#define TK_EXCEPT 106 -#define TK_SELECT 107 -#define TK_DISTINCT 108 -#define TK_DOT 109 -#define TK_FROM 110 -#define TK_JOIN 111 -#define TK_USING 112 -#define TK_ORDER 113 -#define TK_BY 114 -#define TK_GROUP 115 -#define TK_HAVING 116 -#define TK_LIMIT 117 -#define TK_WHERE 118 -#define TK_INTO 119 -#define TK_VALUES 120 -#define TK_INTEGER 121 -#define TK_FLOAT 122 -#define TK_BLOB 123 -#define TK_VARIABLE 124 -#define TK_CASE 125 -#define TK_WHEN 126 -#define TK_THEN 127 -#define TK_ELSE 128 -#define TK_INDEX 129 diff --git a/kopete/plugins/statistics/sqlite/pragma.c b/kopete/plugins/statistics/sqlite/pragma.c deleted file mode 100644 index 94a21863..00000000 --- a/kopete/plugins/statistics/sqlite/pragma.c +++ /dev/null @@ -1,754 +0,0 @@ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the PRAGMA command. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include <ctype.h> - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -# include "pager.h" -# include "btree.h" -#endif - -/* -** Interpret the given string as a boolean value. -*/ -static int getBoolean(const u8 *z){ - static const u8 *azTrue[] = { "yes", "on", "true" }; - int i; - if( z[0]==0 ) return 0; - if( sqlite3IsNumber(z, 0, SQLITE_UTF8) ){ - return atoi(z); - } - for(i=0; i<sizeof(azTrue)/sizeof(azTrue[0]); i++){ - if( sqlite3StrICmp(z,azTrue[i])==0 ) return 1; - } - return 0; -} - -/* -** Interpret the given string as a safety level. Return 0 for OFF, -** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or -** unrecognized string argument. -** -** Note that the values returned are one less that the values that -** should be passed into sqlite3BtreeSetSafetyLevel(). The is done -** to support legacy SQL code. The safety level used to be boolean -** and older scripts may have used numbers 0 for OFF and 1 for ON. -*/ -static int getSafetyLevel(u8 *z){ - static const struct { - const u8 *zWord; - int val; - } aKey[] = { - { "no", 0 }, - { "off", 0 }, - { "false", 0 }, - { "yes", 1 }, - { "on", 1 }, - { "true", 1 }, - { "full", 2 }, - }; - int i; - if( z[0]==0 ) return 1; - if( sqlite3IsNumber(z, 0, SQLITE_UTF8) ){ - return atoi(z); - } - for(i=0; i<sizeof(aKey)/sizeof(aKey[0]); i++){ - if( sqlite3StrICmp(z,aKey[i].zWord)==0 ) return aKey[i].val; - } - return 1; -} - -/* -** Interpret the given string as a temp db location. Return 1 for file -** backed temporary databases, 2 for the Red-Black tree in memory database -** and 0 to use the compile-time default. -*/ -static int getTempStore(const char *z){ - if( z[0]>='0' && z[0]<='2' ){ - return z[0] - '0'; - }else if( sqlite3StrICmp(z, "file")==0 ){ - return 1; - }else if( sqlite3StrICmp(z, "memory")==0 ){ - return 2; - }else{ - return 0; - } -} - -/* -** If the TEMP database is open, close it and mark the database schema -** as needing reloading. This must be done when using the TEMP_STORE -** or DEFAULT_TEMP_STORE pragmas. -*/ -static int changeTempStorage(Parse *pParse, const char *zStorageType){ - int ts = getTempStore(zStorageType); - sqlite3 *db = pParse->db; - if( db->temp_store==ts ) return SQLITE_OK; - if( db->aDb[1].pBt!=0 ){ - if( db->flags & SQLITE_InTrans ){ - sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " - "from within a transaction"); - return SQLITE_ERROR; - } - sqlite3BtreeClose(db->aDb[1].pBt); - db->aDb[1].pBt = 0; - sqlite3ResetInternalSchema(db, 0); - } - db->temp_store = ts; - return SQLITE_OK; -} - -/* -** Generate code to return a single integer value. -*/ -static void returnSingleInt(Parse *pParse, const char *zLabel, int value){ - Vdbe *v = sqlite3GetVdbe(pParse); - sqlite3VdbeAddOp(v, OP_Integer, value, 0); - if( pParse->explain==0 ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, zLabel, P3_STATIC); - } - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); -} - -/* -** Check to see if zRight and zLeft refer to a pragma that queries -** or changes one of the flags in db->flags. Return 1 if so and 0 if not. -** Also, implement the pragma. -*/ -static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ - static const struct { - const char *zName; /* Name of the pragma */ - int mask; /* Mask for the db->flags value */ - } aPragma[] = { - { "vdbe_trace", SQLITE_VdbeTrace }, - { "sql_trace", SQLITE_SqlTrace }, - { "vdbe_listing", SQLITE_VdbeListing }, -#if 1 /* FIX ME: Remove the following pragmas */ - { "full_column_names", SQLITE_FullColNames }, - { "short_column_names", SQLITE_ShortColNames }, - { "count_changes", SQLITE_CountRows }, - { "empty_result_callbacks", SQLITE_NullCallback }, -#endif - }; - int i; - for(i=0; i<sizeof(aPragma)/sizeof(aPragma[0]); i++){ - if( sqlite3StrICmp(zLeft, aPragma[i].zName)==0 ){ - sqlite3 *db = pParse->db; - Vdbe *v; - if( zRight==0 ){ - v = sqlite3GetVdbe(pParse); - if( v ){ - returnSingleInt(pParse, - aPragma[i].zName, (db->flags&aPragma[i].mask)!=0); - } - }else if( getBoolean(zRight) ){ - db->flags |= aPragma[i].mask; - }else{ - db->flags &= ~aPragma[i].mask; - } - return 1; - } - } - return 0; -} - -/* -** Process a pragma statement. -** -** Pragmas are of this form: -** -** PRAGMA [database.]id [= value] -** -** The identifier might also be a string. The value is a string, and -** identifier, or a number. If minusFlag is true, then the value is -** a number that was preceded by a minus sign. -** -** If the left side is "database.id" then pId1 is the database name -** and pId2 is the id. If the left side is just "id" then pId1 is the -** id and pId2 is any empty string. -*/ -void sqlite3Pragma( - Parse *pParse, - Token *pId1, /* First part of [database.]id field */ - Token *pId2, /* Second part of [database.]id field, or NULL */ - Token *pValue, /* Token for <value>, or NULL */ - int minusFlag /* True if a '-' sign preceded <value> */ -){ - char *zLeft = 0; /* Nul-terminated UTF-8 string <id> */ - char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ - const char *zDb = 0; /* The database name */ - Token *pId; /* Pointer to <id> token */ - int iDb; /* Database index for <database> */ - sqlite3 *db = pParse->db; - Db *pDb; - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - - /* Interpret the [database.] part of the pragma statement. iDb is the - ** index of the database this pragma is being applied to in db.aDb[]. */ - iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId); - if( iDb<0 ) return; - pDb = &db->aDb[iDb]; - - zLeft = sqlite3NameFromToken(pId); - if( !zLeft ) return; - if( minusFlag ){ - zRight = sqlite3MPrintf("-%T", pValue); - }else{ - zRight = sqlite3NameFromToken(pValue); - } - - zDb = ((iDb>0)?pDb->zName:0); - if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){ - goto pragma_out; - } - - /* - ** PRAGMA [database.]default_cache_size - ** PRAGMA [database.]default_cache_size=N - ** - ** The first form reports the current persistent setting for the - ** page cache size. The value returned is the maximum number of - ** pages in the page cache. The second form sets both the current - ** page cache size value and the persistent page cache size value - ** stored in the database file. - ** - ** The default cache size is stored in meta-value 2 of page 1 of the - ** database file. The cache size is actually the absolute value of - ** this memory location. The sign of meta-value 2 determines the - ** synchronous setting. A negative value means synchronous is off - ** and a positive value means synchronous is on. - */ - if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ - static const VdbeOpList getCacheSize[] = { - { OP_ReadCookie, 0, 2, 0}, /* 0 */ - { OP_AbsValue, 0, 0, 0}, - { OP_Dup, 0, 0, 0}, - { OP_Integer, 0, 0, 0}, - { OP_Ne, 0, 6, 0}, - { OP_Integer, 0, 0, 0}, /* 5 */ - { OP_Callback, 1, 0, 0}, - }; - int addr; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - if( !zRight ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "cache_size", P3_STATIC); - addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); - sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+5, MAX_PAGES); - }else{ - int size = atoi(zRight); - if( size<0 ) size = -size; - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3VdbeAddOp(v, OP_Integer, size, 0); - sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 2); - addr = sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp(v, OP_Ge, 0, addr+3); - sqlite3VdbeAddOp(v, OP_Negative, 0, 0); - sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 2); - pDb->cache_size = size; - sqlite3BtreeSetCacheSize(pDb->pBt, pDb->cache_size); - } - }else - - /* - ** PRAGMA [database.]page_size - ** PRAGMA [database.]page_size=N - ** - ** The first form reports the current setting for the - ** database page size in bytes. The second form sets the - ** database page size value. The value can only be set if - ** the database has not yet been created. - */ - if( sqlite3StrICmp(zLeft,"page_size")==0 ){ - Btree *pBt = pDb->pBt; - if( !zRight ){ - int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0; - returnSingleInt(pParse, "page_size", size); - }else{ - sqlite3BtreeSetPageSize(pBt, atoi(zRight), sqlite3BtreeGetReserve(pBt)); - } - }else - - /* - ** PRAGMA [database.]cache_size - ** PRAGMA [database.]cache_size=N - ** - ** The first form reports the current local setting for the - ** page cache size. The local setting can be different from - ** the persistent cache size value that is stored in the database - ** file itself. The value returned is the maximum number of - ** pages in the page cache. The second form sets the local - ** page cache size value. It does not change the persistent - ** cache size stored on the disk so the cache size will revert - ** to its default value when the database is closed and reopened. - ** N should be a positive integer. - */ - if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - if( !zRight ){ - returnSingleInt(pParse, "cache_size", pDb->cache_size); - }else{ - int size = atoi(zRight); - if( size<0 ) size = -size; - pDb->cache_size = size; - sqlite3BtreeSetCacheSize(pDb->pBt, pDb->cache_size); - } - }else - - /* - ** PRAGMA temp_store - ** PRAGMA temp_store = "default"|"memory"|"file" - ** - ** Return or set the local value of the temp_store flag. Changing - ** the local value does not make changes to the disk file and the default - ** value will be restored the next time the database is opened. - ** - ** Note that it is possible for the library compile-time options to - ** override this setting - */ - if( sqlite3StrICmp(zLeft, "temp_store")==0 ){ - if( !zRight ){ - returnSingleInt(pParse, "temp_store", db->temp_store); - }else{ - changeTempStorage(pParse, zRight); - } - }else - - /* - ** PRAGMA [database.]synchronous - ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL - ** - ** Return or set the local value of the synchronous flag. Changing - ** the local value does not make changes to the disk file and the - ** default value will be restored the next time the database is - ** opened. - */ - if( sqlite3StrICmp(zLeft,"synchronous")==0 ){ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - if( !zRight ){ - returnSingleInt(pParse, "synchronous", pDb->safety_level-1); - }else{ - if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, - "Safety level may not be changed inside a transaction"); - }else{ - pDb->safety_level = getSafetyLevel(zRight)+1; - sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level); - } - } - }else - -#if 0 /* Used once during development. No longer needed */ - if( sqlite3StrICmp(zLeft, "trigger_overhead_test")==0 ){ - if( getBoolean(zRight) ){ - sqlite3_always_code_trigger_setup = 1; - }else{ - sqlite3_always_code_trigger_setup = 0; - } - }else -#endif - - if( flagPragma(pParse, zLeft, zRight) ){ - /* The flagPragma() subroutine also generates any necessary code - ** there is nothing more to do here */ - }else - - /* - ** PRAGMA table_info(<table>) - ** - ** Return a single row for each column of the named table. The columns of - ** the returned data set are: - ** - ** cid: Column id (numbered from left to right, starting at 0) - ** name: Column name - ** type: Column declaration type. - ** notnull: True if 'NOT NULL' is part of column declaration - ** dflt_value: The default value for the column, if any. - */ - if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - int i; - sqlite3VdbeSetNumCols(v, 6); - sqlite3VdbeSetColName(v, 0, "cid", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "name", P3_STATIC); - sqlite3VdbeSetColName(v, 2, "type", P3_STATIC); - sqlite3VdbeSetColName(v, 3, "notnull", P3_STATIC); - sqlite3VdbeSetColName(v, 4, "dflt_value", P3_STATIC); - sqlite3VdbeSetColName(v, 5, "pk", P3_STATIC); - sqlite3ViewGetColumnNames(pParse, pTab); - for(i=0; i<pTab->nCol; i++){ - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zName, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, - pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", 0); - sqlite3VdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, - pTab->aCol[i].zDflt, P3_STATIC); - sqlite3VdbeAddOp(v, OP_Integer, pTab->aCol[i].isPrimKey, 0); - sqlite3VdbeAddOp(v, OP_Callback, 6, 0); - } - } - }else - - if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){ - Index *pIdx; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pIdx = sqlite3FindIndex(db, zRight, zDb); - if( pIdx ){ - int i; - pTab = pIdx->pTable; - sqlite3VdbeSetNumCols(v, 3); - sqlite3VdbeSetColName(v, 0, "seqno", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "cid", P3_STATIC); - sqlite3VdbeSetColName(v, 2, "name", P3_STATIC); - for(i=0; i<pIdx->nColumn; i++){ - int cnum = pIdx->aiColumn[i]; - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - sqlite3VdbeAddOp(v, OP_Integer, cnum, 0); - assert( pTab->nCol>cnum ); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[cnum].zName, 0); - sqlite3VdbeAddOp(v, OP_Callback, 3, 0); - } - } - }else - - if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){ - Index *pIdx; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - v = sqlite3GetVdbe(pParse); - pIdx = pTab->pIndex; - if( pIdx ){ - int i = 0; - sqlite3VdbeSetNumCols(v, 3); - sqlite3VdbeSetColName(v, 0, "seq", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "name", P3_STATIC); - sqlite3VdbeSetColName(v, 2, "unique", P3_STATIC); - while(pIdx){ - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0); - sqlite3VdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0); - sqlite3VdbeAddOp(v, OP_Callback, 3, 0); - ++i; - pIdx = pIdx->pNext; - } - } - } - }else - - if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){ - FKey *pFK; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - v = sqlite3GetVdbe(pParse); - pFK = pTab->pFKey; - if( pFK ){ - int i = 0; - sqlite3VdbeSetNumCols(v, 5); - sqlite3VdbeSetColName(v, 0, "id", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "seq", P3_STATIC); - sqlite3VdbeSetColName(v, 2, "table", P3_STATIC); - sqlite3VdbeSetColName(v, 3, "from", P3_STATIC); - sqlite3VdbeSetColName(v, 4, "to", P3_STATIC); - while(pFK){ - int j; - for(j=0; j<pFK->nCol; j++){ - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - sqlite3VdbeAddOp(v, OP_Integer, j, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pFK->zTo, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, - pTab->aCol[pFK->aCol[j].iFrom].zName, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, pFK->aCol[j].zCol, 0); - sqlite3VdbeAddOp(v, OP_Callback, 5, 0); - } - ++i; - pFK = pFK->pNextFrom; - } - } - } - }else - - if( sqlite3StrICmp(zLeft, "database_list")==0 ){ - int i; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 3); - sqlite3VdbeSetColName(v, 0, "seq", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "name", P3_STATIC); - sqlite3VdbeSetColName(v, 2, "file", P3_STATIC); - for(i=0; i<db->nDb; i++){ - if( db->aDb[i].pBt==0 ) continue; - assert( db->aDb[i].zName!=0 ); - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, db->aDb[i].zName, 0); - sqlite3VdbeOp3(v, OP_String8, 0, 0, - sqlite3BtreeGetFilename(db->aDb[i].pBt), 0); - sqlite3VdbeAddOp(v, OP_Callback, 3, 0); - } - }else - -#ifndef NDEBUG - if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ - extern void sqlite3ParserTrace(FILE*, char *); - if( getBoolean(zRight) ){ - sqlite3ParserTrace(stdout, "parser: "); - }else{ - sqlite3ParserTrace(0, 0); - } - }else -#endif - - if( sqlite3StrICmp(zLeft, "integrity_check")==0 ){ - int i, j, addr; - - /* Code that initializes the integrity check program. Set the - ** error count 0 - */ - static const VdbeOpList initCode[] = { - { OP_Integer, 0, 0, 0}, - { OP_MemStore, 0, 1, 0}, - }; - - /* Code that appears at the end of the integrity check. If no error - ** messages have been generated, output OK. Otherwise output the - ** error message - */ - static const VdbeOpList endCode[] = { - { OP_MemLoad, 0, 0, 0}, - { OP_Integer, 0, 0, 0}, - { OP_Ne, 0, 0, 0}, /* 2 */ - { OP_String8, 0, 0, "ok"}, - { OP_Callback, 1, 0, 0}, - }; - - /* Initialize the VDBE program */ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "integrity_check", P3_STATIC); - sqlite3VdbeAddOpList(v, ArraySize(initCode), initCode); - - /* Do an integrity check on each database file */ - for(i=0; i<db->nDb; i++){ - HashElem *x; - int cnt = 0; - - sqlite3CodeVerifySchema(pParse, i); - - /* Do an integrity check of the B-Tree - */ - for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0); - cnt++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto pragma_out; - sqlite3VdbeAddOp(v, OP_Integer, pIdx->tnum, 0); - cnt++; - } - } - assert( cnt>0 ); - sqlite3VdbeAddOp(v, OP_IntegrityCk, cnt, i); - sqlite3VdbeAddOp(v, OP_Dup, 0, 1); - addr = sqlite3VdbeOp3(v, OP_String8, 0, 0, "ok", P3_STATIC); - sqlite3VdbeAddOp(v, OP_Eq, 0, addr+6); - sqlite3VdbeOp3(v, OP_String8, 0, 0, - sqlite3MPrintf("*** in database %s ***\n", db->aDb[i].zName), - P3_DYNAMIC); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - sqlite3VdbeAddOp(v, OP_Concat, 0, 1); - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); - - /* Make sure all the indices are constructed correctly. - */ - sqlite3CodeVerifySchema(pParse, i); - for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - int loopTop; - - if( pTab->pIndex==0 ) continue; - sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead); - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp(v, OP_MemStore, 1, 1); - loopTop = sqlite3VdbeAddOp(v, OP_Rewind, 1, 0); - sqlite3VdbeAddOp(v, OP_MemIncr, 1, 0); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int jmp2; - static const VdbeOpList idxErr[] = { - { OP_MemIncr, 0, 0, 0}, - { OP_String8, 0, 0, "rowid "}, - { OP_Recno, 1, 0, 0}, - { OP_String8, 0, 0, " missing from index "}, - { OP_String8, 0, 0, 0}, /* 4 */ - { OP_Concat, 2, 0, 0}, - { OP_Callback, 1, 0, 0}, - }; - sqlite3GenerateIndexKey(v, pIdx, 1); - jmp2 = sqlite3VdbeAddOp(v, OP_Found, j+2, 0); - addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); - sqlite3VdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC); - sqlite3VdbeChangeP2(v, jmp2, sqlite3VdbeCurrentAddr(v)); - } - sqlite3VdbeAddOp(v, OP_Next, 1, loopTop+1); - sqlite3VdbeChangeP2(v, loopTop, sqlite3VdbeCurrentAddr(v)); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - static const VdbeOpList cntIdx[] = { - { OP_Integer, 0, 0, 0}, - { OP_MemStore, 2, 1, 0}, - { OP_Rewind, 0, 0, 0}, /* 2 */ - { OP_MemIncr, 2, 0, 0}, - { OP_Next, 0, 0, 0}, /* 4 */ - { OP_MemLoad, 1, 0, 0}, - { OP_MemLoad, 2, 0, 0}, - { OP_Eq, 0, 0, 0}, /* 7 */ - { OP_MemIncr, 0, 0, 0}, - { OP_String8, 0, 0, "wrong # of entries in index "}, - { OP_String8, 0, 0, 0}, /* 10 */ - { OP_Concat, 0, 0, 0}, - { OP_Callback, 1, 0, 0}, - }; - if( pIdx->tnum==0 ) continue; - addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx); - sqlite3VdbeChangeP1(v, addr+2, j+2); - sqlite3VdbeChangeP2(v, addr+2, addr+5); - sqlite3VdbeChangeP1(v, addr+4, j+2); - sqlite3VdbeChangeP2(v, addr+4, addr+3); - sqlite3VdbeChangeP2(v, addr+7, addr+ArraySize(cntIdx)); - sqlite3VdbeChangeP3(v, addr+10, pIdx->zName, P3_STATIC); - } - } - } - addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode); - sqlite3VdbeChangeP2(v, addr+2, addr+ArraySize(endCode)); - }else - /* - ** PRAGMA encoding - ** PRAGMA encoding = "utf-8"|"utf-16"|"utf-16le"|"utf-16be" - ** - ** In it's first form, this pragma returns the encoding of the main - ** database. If the database is not initialized, it is initialized now. - ** - ** The second form of this pragma is a no-op if the main database file - ** has not already been initialized. In this case it sets the default - ** encoding that will be used for the main database file if a new file - ** is created. If an existing main database file is opened, then the - ** default text encoding for the existing database is used. - ** - ** In all cases new databases created using the ATTACH command are - ** created to use the same default text encoding as the main database. If - ** the main database has not been initialized and/or created when ATTACH - ** is executed, this is done before the ATTACH operation. - ** - ** In the second form this pragma sets the text encoding to be used in - ** new database files created using this database handle. It is only - ** useful if invoked immediately after the main database i - */ - if( sqlite3StrICmp(zLeft, "encoding")==0 ){ - static struct EncName { - char *zName; - u8 enc; - } encnames[] = { - { "UTF-8", SQLITE_UTF8 }, - { "UTF8", SQLITE_UTF8 }, - { "UTF-16le", SQLITE_UTF16LE }, - { "UTF16le", SQLITE_UTF16LE }, - { "UTF-16be", SQLITE_UTF16BE }, - { "UTF16be", SQLITE_UTF16BE }, - { "UTF-16", 0 /* Filled in at run-time */ }, - { "UTF16", 0 /* Filled in at run-time */ }, - { 0, 0 } - }; - struct EncName *pEnc; - encnames[6].enc = encnames[7].enc = SQLITE_UTF16NATIVE; - if( !zRight ){ /* "PRAGMA encoding" */ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "encoding", P3_STATIC); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ - if( pEnc->enc==pParse->db->enc ){ - sqlite3VdbeChangeP3(v, -1, pEnc->zName, P3_STATIC); - break; - } - } - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); - }else{ /* "PRAGMA encoding = XXX" */ - /* Only change the value of sqlite.enc if the database handle is not - ** initialized. If the main database exists, the new sqlite.enc value - ** will be overwritten when the schema is next loaded. If it does not - ** already exists, it will be created to use the new encoding value. - */ - if( !(pParse->db->flags&SQLITE_Initialized) ){ - for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ - if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ - pParse->db->enc = pEnc->enc; - break; - } - } - if( !pEnc->zName ){ - sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight); - } - } - } - }else - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - /* - ** Report the current state of file logs for all databases - */ - if( sqlite3StrICmp(zLeft, "lock_status")==0 ){ - static const char *const azLockName[] = { - "unlocked", "shared", "reserved", "pending", "exclusive" - }; - int i; - Vdbe *v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 2); - sqlite3VdbeSetColName(v, 0, "database", P3_STATIC); - sqlite3VdbeSetColName(v, 1, "status", P3_STATIC); - for(i=0; i<db->nDb; i++){ - Btree *pBt; - Pager *pPager; - if( db->aDb[i].zName==0 ) continue; - sqlite3VdbeOp3(v, OP_String, 0, 0, db->aDb[i].zName, P3_STATIC); - pBt = db->aDb[i].pBt; - if( pBt==0 || (pPager = sqlite3BtreePager(pBt))==0 ){ - sqlite3VdbeOp3(v, OP_String, 0, 0, "closed", P3_STATIC); - }else{ - int j = sqlite3pager_lockstate(pPager); - sqlite3VdbeOp3(v, OP_String, 0, 0, - (j>=0 && j<=4) ? azLockName[j] : "unknown", P3_STATIC); - } - sqlite3VdbeAddOp(v, OP_Callback, 2, 0); - } - }else -#endif - - {} -pragma_out: - sqliteFree(zLeft); - sqliteFree(zRight); -} diff --git a/kopete/plugins/statistics/sqlite/printf.c b/kopete/plugins/statistics/sqlite/printf.c deleted file mode 100644 index 43e12863..00000000 --- a/kopete/plugins/statistics/sqlite/printf.c +++ /dev/null @@ -1,825 +0,0 @@ -/* -** The "printf" code that follows dates from the 1980's. It is in -** the public domain. The original comments are included here for -** completeness. They are very out-of-date but might be useful as -** an historical reference. Most of the "enhancements" have been backed -** out so that the functionality is now the same as standard printf(). -** -************************************************************************** -** -** The following modules is an enhanced replacement for the "printf" subroutines -** found in the standard C library. The following enhancements are -** supported: -** -** + Additional functions. The standard set of "printf" functions -** includes printf, fprintf, sprintf, vprintf, vfprintf, and -** vsprintf. This module adds the following: -** -** * snprintf -- Works like sprintf, but has an extra argument -** which is the size of the buffer written to. -** -** * mprintf -- Similar to sprintf. Writes output to memory -** obtained from malloc. -** -** * xprintf -- Calls a function to dispose of output. -** -** * nprintf -- No output, but returns the number of characters -** that would have been output by printf. -** -** * A v- version (ex: vsnprintf) of every function is also -** supplied. -** -** + A few extensions to the formatting notation are supported: -** -** * The "=" flag (similar to "-") causes the output to be -** be centered in the appropriately sized field. -** -** * The %b field outputs an integer in binary notation. -** -** * The %c field now accepts a precision. The character output -** is repeated by the number of times the precision specifies. -** -** * The %' field works like %c, but takes as its character the -** next character of the format string, instead of the next -** argument. For example, printf("%.78'-") prints 78 minus -** signs, the same as printf("%.78c",'-'). -** -** + When compiled using GCC on a SPARC, this version of printf is -** faster than the library printf for SUN OS 4.1. -** -** + All functions are fully reentrant. -** -*/ -#include "sqliteInt.h" - -/* -** Conversion types fall into various categories as defined by the -** following enumeration. -*/ -#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ -#define etFLOAT 2 /* Floating point. %f */ -#define etEXP 3 /* Exponentional notation. %e and %E */ -#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ -#define etSIZE 5 /* Return number of characters processed so far. %n */ -#define etSTRING 6 /* Strings. %s */ -#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ -#define etPERCENT 8 /* Percent symbol. %% */ -#define etCHARX 9 /* Characters. %c */ -#define etERROR 10 /* Used to indicate no such conversion type */ -/* The rest are extensions, not normally found in printf() */ -#define etCHARLIT 11 /* Literal characters. %' */ -#define etSQLESCAPE 12 /* Strings with '\'' doubled. %q */ -#define etSQLESCAPE2 13 /* Strings with '\'' doubled and enclosed in '', - NULL pointers replaced by SQL NULL. %Q */ -#define etTOKEN 14 /* a pointer to a Token structure */ -#define etSRCLIST 15 /* a pointer to a SrcList */ -#define etPOINTER 16 /* The %p conversion */ - - -/* -** An "etByte" is an 8-bit unsigned value. -*/ -typedef unsigned char etByte; - -/* -** Each builtin conversion character (ex: the 'd' in "%d") is described -** by an instance of the following structure -*/ -typedef struct et_info { /* Information about each format field */ - char fmttype; /* The format field code letter */ - etByte base; /* The base for radix conversion */ - etByte flags; /* One or more of FLAG_ constants below */ - etByte type; /* Conversion paradigm */ - etByte charset; /* Offset into aDigits[] of the digits string */ - etByte prefix; /* Offset into aPrefix[] of the prefix string */ -} et_info; - -/* -** Allowed values for et_info.flags -*/ -#define FLAG_SIGNED 1 /* True if the value to convert is signed */ -#define FLAG_INTERN 2 /* True if for internal use only */ - - -/* -** The following table is searched linearly, so it is good to put the -** most frequently used conversion types first. -*/ -static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; -static const char aPrefix[] = "-x0\000X0"; -static const et_info fmtinfo[] = { - { 'd', 10, 1, etRADIX, 0, 0 }, - { 's', 0, 0, etSTRING, 0, 0 }, - { 'z', 0, 2, etDYNSTRING, 0, 0 }, - { 'q', 0, 0, etSQLESCAPE, 0, 0 }, - { 'Q', 0, 0, etSQLESCAPE2, 0, 0 }, - { 'c', 0, 0, etCHARX, 0, 0 }, - { 'o', 8, 0, etRADIX, 0, 2 }, - { 'u', 10, 0, etRADIX, 0, 0 }, - { 'x', 16, 0, etRADIX, 16, 1 }, - { 'X', 16, 0, etRADIX, 0, 4 }, - { 'f', 0, 1, etFLOAT, 0, 0 }, - { 'e', 0, 1, etEXP, 30, 0 }, - { 'E', 0, 1, etEXP, 14, 0 }, - { 'g', 0, 1, etGENERIC, 30, 0 }, - { 'G', 0, 1, etGENERIC, 14, 0 }, - { 'i', 10, 1, etRADIX, 0, 0 }, - { 'n', 0, 0, etSIZE, 0, 0 }, - { '%', 0, 0, etPERCENT, 0, 0 }, - { 'p', 16, 0, etPOINTER, 0, 1 }, - { 'T', 0, 2, etTOKEN, 0, 0 }, - { 'S', 0, 2, etSRCLIST, 0, 0 }, -}; -#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) - -/* -** If NOFLOATINGPOINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef etNOFLOATINGPOINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)++ >= 16 ) return '0'; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return digit; -} -#endif - -#define etBUFSIZE 1000 /* Size of the output buffer */ - -/* -** The root program. All variations call this core. -** -** INPUTS: -** func This is a pointer to a function taking three arguments -** 1. A pointer to anything. Same as the "arg" parameter. -** 2. A pointer to the list of characters to be output -** (Note, this list is NOT null terminated.) -** 3. An integer number of characters to be output. -** (Note: This number might be zero.) -** -** arg This is the pointer to anything which will be passed as the -** first argument to "func". Use it for whatever you like. -** -** fmt This is the format string, as in the usual print. -** -** ap This is a pointer to a list of arguments. Same as in -** vfprint. -** -** OUTPUTS: -** The return value is the total number of characters sent to -** the function "func". Returns -1 on a error. -** -** Note that the order in which automatic variables are declared below -** seems to make a big difference in determining how fast this beast -** will run. -*/ -static int vxprintf( - void (*func)(void*,const char*,int), /* Consumer of text */ - void *arg, /* First argument to the consumer */ - int useExtended, /* Allow extended %-conversions */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ -){ - int c; /* Next character in the format string */ - char *bufpt; /* Pointer to the conversion buffer */ - int precision; /* Precision of the current field */ - int length; /* Length of the field */ - int idx; /* A general purpose loop counter */ - int count; /* Total number of characters output */ - int width; /* Width of the current field */ - etByte flag_leftjustify; /* True if "-" flag is present */ - etByte flag_plussign; /* True if "+" flag is present */ - etByte flag_blanksign; /* True if " " flag is present */ - etByte flag_alternateform; /* True if "#" flag is present */ - etByte flag_zeropad; /* True if field width constant starts with zero */ - etByte flag_long; /* True if "l" flag is present */ - etByte flag_longlong; /* True if the "ll" flag is present */ - UINT64_TYPE longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ - const et_info *infop; /* Pointer to the appropriate info structure */ - char buf[etBUFSIZE]; /* Conversion buffer */ - char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ - etByte errorflag = 0; /* True if an error is encountered */ - etByte xtype; /* Conversion paradigm */ - char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ - static const char spaces[] = - " "; -#define etSPACESIZE (sizeof(spaces)-1) -#ifndef etNOFLOATINGPOINT - int exp; /* exponent of real numbers */ - double rounder; /* Used for rounding floating point values */ - etByte flag_dp; /* True if decimal point should be shown */ - etByte flag_rtz; /* True if trailing zeros should be removed */ - etByte flag_exp; /* True to force display of the exponent */ - int nsd; /* Number of significant digits returned */ -#endif - - func(arg,"",0); - count = length = 0; - bufpt = 0; - for(; (c=(*fmt))!=0; ++fmt){ - if( c!='%' ){ - int amt; - bufpt = (char *)fmt; - amt = 1; - while( (c=(*++fmt))!='%' && c!=0 ) amt++; - (*func)(arg,bufpt,amt); - count += amt; - if( c==0 ) break; - } - if( (c=(*++fmt))==0 ){ - errorflag = 1; - (*func)(arg,"%",1); - count++; - break; - } - /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = - flag_alternateform = flag_zeropad = 0; - do{ - switch( c ){ - case '-': flag_leftjustify = 1; c = 0; break; - case '+': flag_plussign = 1; c = 0; break; - case ' ': flag_blanksign = 1; c = 0; break; - case '#': flag_alternateform = 1; c = 0; break; - case '0': flag_zeropad = 1; c = 0; break; - default: break; - } - }while( c==0 && (c=(*++fmt))!=0 ); - /* Get the field width */ - width = 0; - if( c=='*' ){ - width = va_arg(ap,int); - if( width<0 ){ - flag_leftjustify = 1; - width = -width; - } - c = *++fmt; - }else{ - while( c>='0' && c<='9' ){ - width = width*10 + c - '0'; - c = *++fmt; - } - } - if( width > etBUFSIZE-10 ){ - width = etBUFSIZE-10; - } - /* Get the precision */ - if( c=='.' ){ - precision = 0; - c = *++fmt; - if( c=='*' ){ - precision = va_arg(ap,int); - if( precision<0 ) precision = -precision; - c = *++fmt; - }else{ - while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; - c = *++fmt; - } - } - /* Limit the precision to prevent overflowing buf[] during conversion */ - if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40; - }else{ - precision = -1; - } - /* Get the conversion type modifier */ - if( c=='l' ){ - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - flag_longlong = 1; - c = *++fmt; - }else{ - flag_longlong = 0; - } - }else{ - flag_long = flag_longlong = 0; - } - /* Fetch the info entry for the field */ - infop = 0; - xtype = etERROR; - for(idx=0; idx<etNINFO; idx++){ - if( c==fmtinfo[idx].fmttype ){ - infop = &fmtinfo[idx]; - if( useExtended || (infop->flags & FLAG_INTERN)==0 ){ - xtype = infop->type; - } - break; - } - } - zExtra = 0; - - /* - ** At this point, variables are initialized as follows: - ** - ** flag_alternateform TRUE if a '#' is present. - ** flag_plussign TRUE if a '+' is present. - ** flag_leftjustify TRUE if a '-' is present or if the - ** field width was negative. - ** flag_zeropad TRUE if the width began with 0. - ** flag_long TRUE if the letter 'l' (ell) prefixed - ** the conversion character. - ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed - ** the conversion character. - ** flag_blanksign TRUE if a ' ' is present. - ** width The specified field width. This is - ** always non-negative. Zero is the default. - ** precision The specified precision. The default - ** is -1. - ** xtype The class of the conversion. - ** infop Pointer to the appropriate info struct. - */ - switch( xtype ){ - case etPOINTER: - flag_longlong = sizeof(char*)==sizeof(i64); - flag_long = sizeof(char*)==sizeof(long int); - /* Fall through into the next case */ - case etRADIX: - if( infop->flags & FLAG_SIGNED ){ - i64 v; - if( flag_longlong ) v = va_arg(ap,i64); - else if( flag_long ) v = va_arg(ap,long int); - else v = va_arg(ap,int); - if( v<0 ){ - longvalue = -v; - prefix = '-'; - }else{ - longvalue = v; - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - }else{ - if( flag_longlong ) longvalue = va_arg(ap,u64); - else if( flag_long ) longvalue = va_arg(ap,unsigned long int); - else longvalue = va_arg(ap,unsigned int); - prefix = 0; - } - if( longvalue==0 ) flag_alternateform = 0; - if( flag_zeropad && precision<width-(prefix!=0) ){ - precision = width-(prefix!=0); - } - bufpt = &buf[etBUFSIZE-1]; - { - register const char *cset; /* Use registers for speed */ - register int base; - cset = &aDigits[infop->charset]; - base = infop->base; - do{ /* Convert to ascii */ - *(--bufpt) = cset[longvalue%base]; - longvalue = longvalue/base; - }while( longvalue>0 ); - } - length = &buf[etBUFSIZE-1]-bufpt; - for(idx=precision-length; idx>0; idx--){ - *(--bufpt) = '0'; /* Zero pad */ - } - if( prefix ) *(--bufpt) = prefix; /* Add sign */ - if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ - const char *pre; - char x; - pre = &aPrefix[infop->prefix]; - if( *bufpt!=pre[0] ){ - for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; - } - } - length = &buf[etBUFSIZE-1]-bufpt; - break; - case etFLOAT: - case etEXP: - case etGENERIC: - realvalue = va_arg(ap,double); -#ifndef etNOFLOATINGPOINT - if( precision<0 ) precision = 6; /* Set default precision */ - if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10; - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - if( infop->type==etGENERIC && precision>0 ) precision--; - rounder = 0.0; -#if 0 - /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ - for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); -#else - /* It makes more sense to use 0.5 */ - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); -#endif - if( infop->type==etFLOAT ) realvalue += rounder; - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( realvalue>0.0 ){ - while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } - while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } - while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } - if( exp>350 || exp<-350 ){ - bufpt = "NaN"; - length = 3; - break; - } - } - bufpt = buf; - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ - flag_exp = xtype==etEXP; - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==etGENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = etEXP; - }else{ - precision = precision - exp; - xtype = etFLOAT; - } - }else{ - flag_rtz = 0; - } - /* - ** The "exp+precision" test causes output to be of type etEXP if - ** the precision is too large to fit in buf[]. - */ - nsd = 0; - if( xtype==etFLOAT && exp+precision<etBUFSIZE-30 ){ - flag_dp = (precision>0 || flag_alternateform); - if( prefix ) *(bufpt++) = prefix; /* Sign */ - if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */ - else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd); - if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */ - for(exp++; exp<0 && precision>0; precision--, exp++){ - *(bufpt++) = '0'; - } - while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd); - *(bufpt--) = 0; /* Null terminate */ - if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ - while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; - if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; - } - bufpt++; /* point to next free slot */ - }else{ /* etEXP or etGENERIC */ - flag_dp = (precision>0 || flag_alternateform); - if( prefix ) *(bufpt++) = prefix; /* Sign */ - *(bufpt++) = et_getdigit(&realvalue,&nsd); /* First digit */ - if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */ - while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd); - bufpt--; /* point to last digit */ - if( flag_rtz && flag_dp ){ /* Remove tail zeros */ - while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; - if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; - } - bufpt++; /* point to next free slot */ - if( exp || flag_exp ){ - *(bufpt++) = aDigits[infop->charset]; - if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */ - else { *(bufpt++) = '+'; } - if( exp>=100 ){ - *(bufpt++) = (exp/100)+'0'; /* 100's digit */ - exp %= 100; - } - *(bufpt++) = exp/10+'0'; /* 10's digit */ - *(bufpt++) = exp%10+'0'; /* 1's digit */ - } - } - /* The converted number is in buf[] and zero terminated. Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions. */ - length = bufpt-buf; - bufpt = buf; - - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } -#endif - break; - case etSIZE: - *(va_arg(ap,int*)) = count; - length = width = 0; - break; - case etPERCENT: - buf[0] = '%'; - bufpt = buf; - length = 1; - break; - case etCHARLIT: - case etCHARX: - c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); - if( precision>=0 ){ - for(idx=1; idx<precision; idx++) buf[idx] = c; - length = precision; - }else{ - length =1; - } - bufpt = buf; - break; - case etSTRING: - case etDYNSTRING: - bufpt = va_arg(ap,char*); - if( bufpt==0 ){ - bufpt = ""; - }else if( xtype==etDYNSTRING ){ - zExtra = bufpt; - } - length = strlen(bufpt); - if( precision>=0 && precision<length ) length = precision; - break; - case etSQLESCAPE: - case etSQLESCAPE2: - { - int i, j, n, c, isnull; - char *arg = va_arg(ap,char*); - isnull = arg==0; - if( isnull ) arg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); - for(i=n=0; (c=arg[i])!=0; i++){ - if( c=='\'' ) n++; - } - n += i + 1 + ((!isnull && xtype==etSQLESCAPE2) ? 2 : 0); - if( n>etBUFSIZE ){ - bufpt = zExtra = sqliteMalloc( n ); - if( bufpt==0 ) return -1; - }else{ - bufpt = buf; - } - j = 0; - if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\''; - for(i=0; (c=arg[i])!=0; i++){ - bufpt[j++] = c; - if( c=='\'' ) bufpt[j++] = c; - } - if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\''; - bufpt[j] = 0; - length = j; - if( precision>=0 && precision<length ) length = precision; - } - break; - case etTOKEN: { - Token *pToken = va_arg(ap, Token*); - if( pToken && pToken->z ){ - (*func)(arg, pToken->z, pToken->n); - } - length = width = 0; - break; - } - case etSRCLIST: { - SrcList *pSrc = va_arg(ap, SrcList*); - int k = va_arg(ap, int); - struct SrcList_item *pItem = &pSrc->a[k]; - assert( k>=0 && k<pSrc->nSrc ); - if( pItem->zDatabase && pItem->zDatabase[0] ){ - (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase)); - (*func)(arg, ".", 1); - } - (*func)(arg, pItem->zName, strlen(pItem->zName)); - length = width = 0; - break; - } - case etERROR: - buf[0] = '%'; - buf[1] = c; - errorflag = 0; - idx = 1+(c!=0); - (*func)(arg,"%",idx); - count += idx; - if( c==0 ) fmt--; - break; - }/* End switch over the format type */ - /* - ** The text of the conversion is pointed to by "bufpt" and is - ** "length" characters long. The field width is "width". Do - ** the output. - */ - if( !flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - count += nspace; - while( nspace>=etSPACESIZE ){ - (*func)(arg,spaces,etSPACESIZE); - nspace -= etSPACESIZE; - } - if( nspace>0 ) (*func)(arg,spaces,nspace); - } - } - if( length>0 ){ - (*func)(arg,bufpt,length); - count += length; - } - if( flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - count += nspace; - while( nspace>=etSPACESIZE ){ - (*func)(arg,spaces,etSPACESIZE); - nspace -= etSPACESIZE; - } - if( nspace>0 ) (*func)(arg,spaces,nspace); - } - } - if( zExtra ){ - sqliteFree(zExtra); - } - }/* End for loop over the format string */ - return errorflag ? -1 : count; -} /* End of function */ - - -/* This structure is used to store state information about the -** write to memory that is currently in progress. -*/ -struct sgMprintf { - char *zBase; /* A base allocation */ - char *zText; /* The string collected so far */ - int nChar; /* Length of the string so far */ - int nTotal; /* Output size if unconstrained */ - int nAlloc; /* Amount of space allocated in zText */ - void *(*xRealloc)(void*,int); /* Function used to realloc memory */ -}; - -/* -** This function implements the callback from vxprintf. -** -** This routine add nNewChar characters of text in zNewText to -** the sgMprintf structure pointed to by "arg". -*/ -static void mout(void *arg, const char *zNewText, int nNewChar){ - struct sgMprintf *pM = (struct sgMprintf*)arg; - pM->nTotal += nNewChar; - if( pM->nChar + nNewChar + 1 > pM->nAlloc ){ - if( pM->xRealloc==0 ){ - nNewChar = pM->nAlloc - pM->nChar - 1; - }else{ - pM->nAlloc = pM->nChar + nNewChar*2 + 1; - if( pM->zText==pM->zBase ){ - pM->zText = pM->xRealloc(0, pM->nAlloc); - if( pM->zText && pM->nChar ){ - memcpy(pM->zText, pM->zBase, pM->nChar); - } - }else{ - pM->zText = pM->xRealloc(pM->zText, pM->nAlloc); - } - } - } - if( pM->zText ){ - if( nNewChar>0 ){ - memcpy(&pM->zText[pM->nChar], zNewText, nNewChar); - pM->nChar += nNewChar; - } - pM->zText[pM->nChar] = 0; - } -} - -/* -** This routine is a wrapper around xprintf() that invokes mout() as -** the consumer. -*/ -static char *base_vprintf( - void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */ - int useInternal, /* Use internal %-conversions if true */ - char *zInitBuf, /* Initially write here, before mallocing */ - int nInitBuf, /* Size of zInitBuf[] */ - const char *zFormat, /* format string */ - va_list ap /* arguments */ -){ - struct sgMprintf sM; - sM.zBase = sM.zText = zInitBuf; - sM.nChar = sM.nTotal = 0; - sM.nAlloc = nInitBuf; - sM.xRealloc = xRealloc; - vxprintf(mout, &sM, useInternal, zFormat, ap); - if( xRealloc ){ - if( sM.zText==sM.zBase ){ - sM.zText = xRealloc(0, sM.nChar+1); - if( sM.zText ){ - memcpy(sM.zText, sM.zBase, sM.nChar+1); - } - }else if( sM.nAlloc>sM.nChar+10 ){ - sM.zText = xRealloc(sM.zText, sM.nChar+1); - } - } - return sM.zText; -} - -/* -** Realloc that is a real function, not a macro. -*/ -static void *printf_realloc(void *old, int size){ - return sqliteRealloc(old,size); -} - -/* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. -*/ -char *sqlite3VMPrintf(const char *zFormat, va_list ap){ - char zBase[1000]; - return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); -} - -/* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. -*/ -char *sqlite3MPrintf(const char *zFormat, ...){ - va_list ap; - char *z; - char zBase[1000]; - va_start(ap, zFormat); - z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); - va_end(ap); - return z; -} - -/* -** Print into memory obtained from malloc(). Do not use the internal -** %-conversion extensions. This routine is for use by external users. -*/ -char *sqlite3_mprintf(const char *zFormat, ...){ - va_list ap; - char *z; - char zBuf[200]; - - va_start(ap,zFormat); - z = base_vprintf((void*(*)(void*,int))realloc, 0, - zBuf, sizeof(zBuf), zFormat, ap); - va_end(ap); - return z; -} - -/* This is the varargs version of sqlite3_mprintf. -*/ -char *sqlite3_vmprintf(const char *zFormat, va_list ap){ - char zBuf[200]; - return base_vprintf((void*(*)(void*,int))realloc, 0, - zBuf, sizeof(zBuf), zFormat, ap); -} - -/* -** sqlite3_snprintf() works like snprintf() except that it ignores the -** current locale settings. This is important for SQLite because we -** are not able to use a "," as the decimal point in place of "." as -** specified by some locales. -*/ -char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; - va_list ap; - - va_start(ap,zFormat); - z = base_vprintf(0, 0, zBuf, n, zFormat, ap); - va_end(ap); - return z; -} - -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/* -** A version of printf() that understands %lld. Used for debugging. -** The printf() built into some versions of windows does not understand %lld -** and segfaults if you give it a long long int. -*/ -void sqlite3DebugPrintf(const char *zFormat, ...){ - extern int getpid(void); - va_list ap; - char zBuf[500]; - va_start(ap, zFormat); - base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap); - va_end(ap); - fprintf(stdout,"%d: %s", getpid(), zBuf); - fflush(stdout); -} -#endif diff --git a/kopete/plugins/statistics/sqlite/random.c b/kopete/plugins/statistics/sqlite/random.c deleted file mode 100644 index de74e291..00000000 --- a/kopete/plugins/statistics/sqlite/random.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement a pseudo-random number -** generator (PRNG) for SQLite. -** -** Random numbers are used by some of the database backends in order -** to generate random integer keys for tables or random filenames. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" - - -/* -** Get a single 8-bit random value from the RC4 PRNG. The Mutex -** must be held while executing this routine. -** -** Why not just use a library random generator like lrand48() for this? -** Because the OP_NewRecno opcode in the VDBE depends on having a very -** good source of random numbers. The lrand48() library function may -** well be good enough. But maybe not. Or maybe lrand48() has some -** subtle problems on some systems that could cause problems. It is hard -** to know. To minimize the risk of problems due to bad lrand48() -** implementations, SQLite uses this random number generator based -** on RC4, which we know works very well. -*/ -static int randomByte(){ - unsigned char t; - - /* All threads share a single random number generator. - ** This structure is the current state of the generator. - */ - static struct { - unsigned char isInit; /* True if initialized */ - unsigned char i, j; /* State variables */ - unsigned char s[256]; /* State variables */ - } prng; - - /* Initialize the state of the random number generator once, - ** the first time this routine is called. The seed value does - ** not need to contain a lot of randomness since we are not - ** trying to do secure encryption or anything like that... - ** - ** Nothing in this file or anywhere else in SQLite does any kind of - ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random - ** number generator) not as an encryption device. - */ - if( !prng.isInit ){ - int i; - char k[256]; - prng.j = 0; - prng.i = 0; - sqlite3OsRandomSeed(k); - for(i=0; i<256; i++){ - prng.s[i] = i; - } - for(i=0; i<256; i++){ - prng.j += prng.s[i] + k[i]; - t = prng.s[prng.j]; - prng.s[prng.j] = prng.s[i]; - prng.s[i] = t; - } - prng.isInit = 1; - } - - /* Generate and return single random byte - */ - prng.i++; - t = prng.s[prng.i]; - prng.j += t; - prng.s[prng.i] = prng.s[prng.j]; - prng.s[prng.j] = t; - t += prng.s[prng.i]; - return prng.s[t]; -} - -/* -** Return N random bytes. -*/ -void sqlite3Randomness(int N, void *pBuf){ - unsigned char *zBuf = pBuf; - sqlite3OsEnterMutex(); - while( N-- ){ - *(zBuf++) = randomByte(); - } - sqlite3OsLeaveMutex(); -} - - - diff --git a/kopete/plugins/statistics/sqlite/select.c b/kopete/plugins/statistics/sqlite/select.c deleted file mode 100644 index 8bee7897..00000000 --- a/kopete/plugins/statistics/sqlite/select.c +++ /dev/null @@ -1,2628 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle SELECT statements in SQLite. -** -** $Id$ -*/ -#include "sqliteInt.h" - - -/* -** Allocate a new Select structure and return a pointer to that -** structure. -*/ -Select *sqlite3SelectNew( - ExprList *pEList, /* which columns to include in the result */ - SrcList *pSrc, /* the FROM clause -- which tables to scan */ - Expr *pWhere, /* the WHERE clause */ - ExprList *pGroupBy, /* the GROUP BY clause */ - Expr *pHaving, /* the HAVING clause */ - ExprList *pOrderBy, /* the ORDER BY clause */ - int isDistinct, /* true if the DISTINCT keyword is present */ - int nLimit, /* LIMIT value. -1 means not used */ - int nOffset /* OFFSET value. 0 means no offset */ -){ - Select *pNew; - pNew = sqliteMalloc( sizeof(*pNew) ); - if( pNew==0 ){ - sqlite3ExprListDelete(pEList); - sqlite3SrcListDelete(pSrc); - sqlite3ExprDelete(pWhere); - sqlite3ExprListDelete(pGroupBy); - sqlite3ExprDelete(pHaving); - sqlite3ExprListDelete(pOrderBy); - }else{ - if( pEList==0 ){ - pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_ALL,0,0,0), 0); - } - pNew->pEList = pEList; - pNew->pSrc = pSrc; - pNew->pWhere = pWhere; - pNew->pGroupBy = pGroupBy; - pNew->pHaving = pHaving; - pNew->pOrderBy = pOrderBy; - pNew->isDistinct = isDistinct; - pNew->op = TK_SELECT; - pNew->nLimit = nLimit; - pNew->nOffset = nOffset; - pNew->iLimit = -1; - pNew->iOffset = -1; - } - return pNew; -} - -/* -** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the -** type of join. Return an integer constant that expresses that type -** in terms of the following bit values: -** -** JT_INNER -** JT_OUTER -** JT_NATURAL -** JT_LEFT -** JT_RIGHT -** -** A full outer join is the combination of JT_LEFT and JT_RIGHT. -** -** If an illegal or unsupported join type is seen, then still return -** a join type, but put an error in the pParse structure. -*/ -int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ - int jointype = 0; - Token *apAll[3]; - Token *p; - static const struct { - const char *zKeyword; - u8 nChar; - u8 code; - } keywords[] = { - { "natural", 7, JT_NATURAL }, - { "left", 4, JT_LEFT|JT_OUTER }, - { "right", 5, JT_RIGHT|JT_OUTER }, - { "full", 4, JT_LEFT|JT_RIGHT|JT_OUTER }, - { "outer", 5, JT_OUTER }, - { "inner", 5, JT_INNER }, - { "cross", 5, JT_INNER }, - }; - int i, j; - apAll[0] = pA; - apAll[1] = pB; - apAll[2] = pC; - for(i=0; i<3 && apAll[i]; i++){ - p = apAll[i]; - for(j=0; j<sizeof(keywords)/sizeof(keywords[0]); j++){ - if( p->n==keywords[j].nChar - && sqlite3StrNICmp(p->z, keywords[j].zKeyword, p->n)==0 ){ - jointype |= keywords[j].code; - break; - } - } - if( j>=sizeof(keywords)/sizeof(keywords[0]) ){ - jointype |= JT_ERROR; - break; - } - } - if( - (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || - (jointype & JT_ERROR)!=0 - ){ - const char *zSp1 = " "; - const char *zSp2 = " "; - if( pB==0 ){ zSp1++; } - if( pC==0 ){ zSp2++; } - sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " - "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC); - jointype = JT_INNER; - }else if( jointype & JT_RIGHT ){ - sqlite3ErrorMsg(pParse, - "RIGHT and FULL OUTER JOINs are not currently supported"); - jointype = JT_INNER; - } - return jointype; -} - -/* -** Return the index of a column in a table. Return -1 if the column -** is not contained in the table. -*/ -static int columnIndex(Table *pTab, const char *zCol){ - int i; - for(i=0; i<pTab->nCol; i++){ - if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; - } - return -1; -} - -/* -** Set the value of a token to a '\000'-terminated string. -*/ -static void setToken(Token *p, const char *z){ - p->z = z; - p->n = strlen(z); - p->dyn = 0; -} - - -/* -** Add a term to the WHERE expression in *ppExpr that requires the -** zCol column to be equal in the two tables pTab1 and pTab2. -*/ -static void addWhereTerm( - const char *zCol, /* Name of the column */ - const Table *pTab1, /* First table */ - const Table *pTab2, /* Second table */ - Expr **ppExpr /* Add the equality term to this expression */ -){ - Token dummy; - Expr *pE1a, *pE1b, *pE1c; - Expr *pE2a, *pE2b, *pE2c; - Expr *pE; - - setToken(&dummy, zCol); - pE1a = sqlite3Expr(TK_ID, 0, 0, &dummy); - pE2a = sqlite3Expr(TK_ID, 0, 0, &dummy); - setToken(&dummy, pTab1->zName); - pE1b = sqlite3Expr(TK_ID, 0, 0, &dummy); - setToken(&dummy, pTab2->zName); - pE2b = sqlite3Expr(TK_ID, 0, 0, &dummy); - pE1c = sqlite3Expr(TK_DOT, pE1b, pE1a, 0); - pE2c = sqlite3Expr(TK_DOT, pE2b, pE2a, 0); - pE = sqlite3Expr(TK_EQ, pE1c, pE2c, 0); - ExprSetProperty(pE, EP_FromJoin); - *ppExpr = sqlite3ExprAnd(*ppExpr, pE); -} - -/* -** Set the EP_FromJoin property on all terms of the given expression. -** -** The EP_FromJoin property is used on terms of an expression to tell -** the LEFT OUTER JOIN processing logic that this term is part of the -** join restriction specified in the ON or USING clause and not a part -** of the more general WHERE clause. These terms are moved over to the -** WHERE clause during join processing but we need to remember that they -** originated in the ON or USING clause. -*/ -static void setJoinExpr(Expr *p){ - while( p ){ - ExprSetProperty(p, EP_FromJoin); - setJoinExpr(p->pLeft); - p = p->pRight; - } -} - -/* -** This routine processes the join information for a SELECT statement. -** ON and USING clauses are converted into extra terms of the WHERE clause. -** NATURAL joins also create extra WHERE clause terms. -** -** The terms of a FROM clause are contained in the Select.pSrc structure. -** The left most table is the first entry in Select.pSrc. The right-most -** table is the last entry. The join operator is held in the entry to -** the left. Thus entry 0 contains the join operator for the join between -** entries 0 and 1. Any ON or USING clauses associated with the join are -** also attached to the left entry. -** -** This routine returns the number of errors encountered. -*/ -static int sqliteProcessJoin(Parse *pParse, Select *p){ - SrcList *pSrc; /* All tables in the FROM clause */ - int i, j; /* Loop counters */ - struct SrcList_item *pLeft; /* Left table being joined */ - struct SrcList_item *pRight; /* Right table being joined */ - - pSrc = p->pSrc; - pLeft = &pSrc->a[0]; - pRight = &pLeft[1]; - for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ - Table *pLeftTab = pLeft->pTab; - Table *pRightTab = pRight->pTab; - - if( pLeftTab==0 || pRightTab==0 ) continue; - - /* When the NATURAL keyword is present, add WHERE clause terms for - ** every column that the two tables have in common. - */ - if( pLeft->jointype & JT_NATURAL ){ - if( pLeft->pOn || pLeft->pUsing ){ - sqlite3ErrorMsg(pParse, "a NATURAL join may not have " - "an ON or USING clause", 0); - return 1; - } - for(j=0; j<pLeftTab->nCol; j++){ - char *zName = pLeftTab->aCol[j].zName; - if( columnIndex(pRightTab, zName)>=0 ){ - addWhereTerm(zName, pLeftTab, pRightTab, &p->pWhere); - } - } - } - - /* Disallow both ON and USING clauses in the same join - */ - if( pLeft->pOn && pLeft->pUsing ){ - sqlite3ErrorMsg(pParse, "cannot have both ON and USING " - "clauses in the same join"); - return 1; - } - - /* Add the ON clause to the end of the WHERE clause, connected by - ** an AND operator. - */ - if( pLeft->pOn ){ - setJoinExpr(pLeft->pOn); - p->pWhere = sqlite3ExprAnd(p->pWhere, pLeft->pOn); - pLeft->pOn = 0; - } - - /* Create extra terms on the WHERE clause for each column named - ** in the USING clause. Example: If the two tables to be joined are - ** A and B and the USING clause names X, Y, and Z, then add this - ** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z - ** Report an error if any column mentioned in the USING clause is - ** not contained in both tables to be joined. - */ - if( pLeft->pUsing ){ - IdList *pList = pLeft->pUsing; - for(j=0; j<pList->nId; j++){ - char *zName = pList->a[j].zName; - if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){ - sqlite3ErrorMsg(pParse, "cannot join using column %s - column " - "not present in both tables", zName); - return 1; - } - addWhereTerm(zName, pLeftTab, pRightTab, &p->pWhere); - } - } - } - return 0; -} - -/* -** Delete the given Select structure and all of its substructures. -*/ -void sqlite3SelectDelete(Select *p){ - if( p==0 ) return; - sqlite3ExprListDelete(p->pEList); - sqlite3SrcListDelete(p->pSrc); - sqlite3ExprDelete(p->pWhere); - sqlite3ExprListDelete(p->pGroupBy); - sqlite3ExprDelete(p->pHaving); - sqlite3ExprListDelete(p->pOrderBy); - sqlite3SelectDelete(p->pPrior); - sqliteFree(p->zSelect); - sqliteFree(p); -} - -/* -** Delete the aggregate information from the parse structure. -*/ -static void sqliteAggregateInfoReset(Parse *pParse){ - sqliteFree(pParse->aAgg); - pParse->aAgg = 0; - pParse->nAgg = 0; - pParse->useAgg = 0; -} - -/* -** Insert code into "v" that will push the record on the top of the -** stack into the sorter. -*/ -static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){ - int i; - for(i=0; i<pOrderBy->nExpr; i++){ - sqlite3ExprCode(pParse, pOrderBy->a[i].pExpr); - } - sqlite3VdbeAddOp(v, OP_MakeRecord, pOrderBy->nExpr, 0); - sqlite3VdbeAddOp(v, OP_SortPut, 0, 0); -} - -/* -** Add code to implement the OFFSET and LIMIT -*/ -static void codeLimiter( - Vdbe *v, /* Generate code into this VM */ - Select *p, /* The SELECT statement being coded */ - int iContinue, /* Jump here to skip the current record */ - int iBreak, /* Jump here to end the loop */ - int nPop /* Number of times to pop stack when jumping */ -){ - if( p->iOffset>=0 ){ - int addr = sqlite3VdbeCurrentAddr(v) + 2; - if( nPop>0 ) addr++; - sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, addr); - if( nPop>0 ){ - sqlite3VdbeAddOp(v, OP_Pop, nPop, 0); - } - sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue); - VdbeComment((v, "# skip OFFSET records")); - } - if( p->iLimit>=0 ){ - sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak); - VdbeComment((v, "# exit when LIMIT reached")); - } -} - -/* -** This routine generates the code for the inside of the inner loop -** of a SELECT. -** -** If srcTab and nColumn are both zero, then the pEList expressions -** are evaluated in order to get the data for this row. If nColumn>0 -** then data is pulled from srcTab and pEList is used only to get the -** datatypes for each column. -*/ -static int selectInnerLoop( - Parse *pParse, /* The parser context */ - Select *p, /* The complete select statement being coded */ - ExprList *pEList, /* List of values being extracted */ - int srcTab, /* Pull data from this table */ - int nColumn, /* Number of columns in the source table */ - ExprList *pOrderBy, /* If not NULL, sort results using this key */ - int distinct, /* If >=0, make sure results are distinct */ - int eDest, /* How to dispose of the results */ - int iParm, /* An argument to the disposal method */ - int iContinue, /* Jump here to continue with next row */ - int iBreak, /* Jump here to break out of the inner loop */ - char *aff /* affinity string if eDest is SRT_Union */ -){ - Vdbe *v = pParse->pVdbe; - int i; - int hasDistinct; /* True if the DISTINCT keyword is present */ - - if( v==0 ) return 0; - assert( pEList!=0 ); - - /* If there was a LIMIT clause on the SELECT statement, then do the check - ** to see if this row should be output. - */ - hasDistinct = distinct>=0 && pEList && pEList->nExpr>0; - if( pOrderBy==0 && !hasDistinct ){ - codeLimiter(v, p, iContinue, iBreak, 0); - } - - /* Pull the requested columns. - */ - if( nColumn>0 ){ - for(i=0; i<nColumn; i++){ - sqlite3VdbeAddOp(v, OP_Column, srcTab, i); - } - }else{ - nColumn = pEList->nExpr; - for(i=0; i<pEList->nExpr; i++){ - sqlite3ExprCode(pParse, pEList->a[i].pExpr); - } - } - - /* If the DISTINCT keyword was present on the SELECT statement - ** and this row has been seen before, then do not make this row - ** part of the result. - */ - if( hasDistinct ){ -#if NULL_ALWAYS_DISTINCT - sqlite3VdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqlite3VdbeCurrentAddr(v)+7); -#endif - /* Deliberately leave the affinity string off of the following - ** OP_MakeRecord */ - sqlite3VdbeAddOp(v, OP_MakeRecord, pEList->nExpr * -1, 0); - sqlite3VdbeAddOp(v, OP_Distinct, distinct, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue); - VdbeComment((v, "# skip indistinct records")); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutStrKey, distinct, 0); - if( pOrderBy==0 ){ - codeLimiter(v, p, iContinue, iBreak, nColumn); - } - } - - switch( eDest ){ - /* In this mode, write each query result to the key of the temporary - ** table iParm. - */ - case SRT_Union: { - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT); - sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutStrKey, iParm, 0); - break; - } - - /* Store the result as data using a unique key. - */ - case SRT_Table: - case SRT_TempTable: { - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); - if( pOrderBy ){ - pushOntoSorter(pParse, v, pOrderBy); - }else{ - sqlite3VdbeAddOp(v, OP_NewRecno, iParm, 0); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, iParm, 0); - } - break; - } - - /* Construct a record from the query result, but instead of - ** saving that record, use it as a key to delete elements from - ** the temporary table iParm. - */ - case SRT_Except: { - int addr; - addr = sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT); - sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC); - sqlite3VdbeAddOp(v, OP_NotFound, iParm, addr+3); - sqlite3VdbeAddOp(v, OP_Delete, iParm, 0); - break; - } - - /* If we are creating a set for an "expr IN (SELECT ...)" construct, - ** then there should be a single item on the stack. Write this - ** item into the set table with bogus data. - */ - case SRT_Set: { - int addr1 = sqlite3VdbeCurrentAddr(v); - int addr2; - - assert( nColumn==1 ); - sqlite3VdbeAddOp(v, OP_NotNull, -1, addr1+3); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - addr2 = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); - if( pOrderBy ){ - pushOntoSorter(pParse, v, pOrderBy); - }else{ - char aff = (iParm>>16)&0xFF; - aff = sqlite3CompareAffinity(pEList->a[0].pExpr, aff); - sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &aff, 1); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutStrKey, (iParm&0x0000FFFF), 0); - } - sqlite3VdbeChangeP2(v, addr2, sqlite3VdbeCurrentAddr(v)); - break; - } - - /* If this is a scalar select that is part of an expression, then - ** store the results in the appropriate memory cell and break out - ** of the scan loop. - */ - case SRT_Mem: { - assert( nColumn==1 ); - if( pOrderBy ){ - pushOntoSorter(pParse, v, pOrderBy); - }else{ - sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1); - sqlite3VdbeAddOp(v, OP_Goto, 0, iBreak); - } - break; - } - - /* Send the data to the callback function. - */ - case SRT_Callback: - case SRT_Sorter: { - if( pOrderBy ){ - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); - pushOntoSorter(pParse, v, pOrderBy); - }else{ - assert( eDest==SRT_Callback ); - sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0); - } - break; - } - - /* Invoke a subroutine to handle the results. The subroutine itself - ** is responsible for popping the results off of the stack. - */ - case SRT_Subroutine: { - if( pOrderBy ){ - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); - pushOntoSorter(pParse, v, pOrderBy); - }else{ - sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm); - } - break; - } - - /* Discard the results. This is used for SELECT statements inside - ** the body of a TRIGGER. The purpose of such selects is to call - ** user-defined functions that have side effects. We do not care - ** about the actual results of the select. - */ - default: { - assert( eDest==SRT_Discard ); - sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0); - break; - } - } - return 0; -} - -/* -** If the inner loop was generated using a non-null pOrderBy argument, -** then the results were placed in a sorter. After the loop is terminated -** we need to run the sorter and output the results. The following -** routine generates the code needed to do that. -*/ -static void generateSortTail( - Parse *pParse, /* The parsing context */ - Select *p, /* The SELECT statement */ - Vdbe *v, /* Generate code into this VDBE */ - int nColumn, /* Number of columns of data */ - int eDest, /* Write the sorted results here */ - int iParm /* Optional parameter associated with eDest */ -){ - int end1 = sqlite3VdbeMakeLabel(v); - int end2 = sqlite3VdbeMakeLabel(v); - int addr; - KeyInfo *pInfo; - ExprList *pOrderBy; - int nCol, i; - sqlite3 *db = pParse->db; - - if( eDest==SRT_Sorter ) return; - pOrderBy = p->pOrderBy; - nCol = pOrderBy->nExpr; - pInfo = sqliteMalloc( sizeof(*pInfo) + nCol*(sizeof(CollSeq*)+1) ); - if( pInfo==0 ) return; - pInfo->aSortOrder = (char*)&pInfo->aColl[nCol]; - pInfo->nField = nCol; - for(i=0; i<nCol; i++){ - /* If a collation sequence was specified explicity, then it - ** is stored in pOrderBy->a[i].zName. Otherwise, use the default - ** collation type for the expression. - */ - pInfo->aColl[i] = sqlite3ExprCollSeq(pParse, pOrderBy->a[i].pExpr); - if( !pInfo->aColl[i] ){ - pInfo->aColl[i] = db->pDfltColl; - } - pInfo->aSortOrder[i] = pOrderBy->a[i].sortOrder; - } - sqlite3VdbeOp3(v, OP_Sort, 0, 0, (char*)pInfo, P3_KEYINFO_HANDOFF); - addr = sqlite3VdbeAddOp(v, OP_SortNext, 0, end1); - codeLimiter(v, p, addr, end2, 1); - switch( eDest ){ - case SRT_Table: - case SRT_TempTable: { - sqlite3VdbeAddOp(v, OP_NewRecno, iParm, 0); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, iParm, 0); - break; - } - case SRT_Set: { - assert( nColumn==1 ); - sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, "n", P3_STATIC); - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_PutStrKey, (iParm&0x0000FFFF), 0); - break; - } - case SRT_Mem: { - assert( nColumn==1 ); - sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1); - sqlite3VdbeAddOp(v, OP_Goto, 0, end1); - break; - } - case SRT_Callback: - case SRT_Subroutine: { - int i; - sqlite3VdbeAddOp(v, OP_Integer, p->pEList->nExpr, 0); - sqlite3VdbeAddOp(v, OP_Pull, 1, 0); - for(i=0; i<nColumn; i++){ - sqlite3VdbeAddOp(v, OP_Column, -1-i, i); - } - if( eDest==SRT_Callback ){ - sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0); - }else{ - sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm); - } - sqlite3VdbeAddOp(v, OP_Pop, 2, 0); - break; - } - default: { - /* Do nothing */ - break; - } - } - sqlite3VdbeAddOp(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end2); - sqlite3VdbeAddOp(v, OP_Pop, 1, 0); - sqlite3VdbeResolveLabel(v, end1); - sqlite3VdbeAddOp(v, OP_SortReset, 0, 0); -} - -/* -** Return a pointer to a string containing the 'declaration type' of the -** expression pExpr. The string may be treated as static by the caller. -** -** If the declaration type is the exact datatype definition extracted from -** the original CREATE TABLE statement if the expression is a column. -** -** The declaration type for an expression is either TEXT, NUMERIC or ANY. -** The declaration type for a ROWID field is INTEGER. -*/ -static const char *columnType(Parse *pParse, SrcList *pTabList, Expr *pExpr){ - char const *zType; - int j; - if( pExpr==0 || pTabList==0 ) return 0; - - switch( pExpr->op ){ - case TK_COLUMN: { - Table *pTab; - int iCol = pExpr->iColumn; - for(j=0; j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable; j++){} - assert( j<pTabList->nSrc ); - pTab = pTabList->a[j].pTab; - if( iCol<0 ) iCol = pTab->iPKey; - assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) ); - if( iCol<0 ){ - zType = "INTEGER"; - }else{ - zType = pTab->aCol[iCol].zType; - } - break; - } - case TK_AS: - zType = columnType(pParse, pTabList, pExpr->pLeft); - break; - case TK_SELECT: { - Select *pS = pExpr->pSelect; - zType = columnType(pParse, pS->pSrc, pS->pEList->a[0].pExpr); - break; - } - default: - zType = 0; - } - - return zType; -} - -/* -** Generate code that will tell the VDBE the declaration types of columns -** in the result set. -*/ -static void generateColumnTypes( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* List of tables */ - ExprList *pEList /* Expressions defining the result set */ -){ - Vdbe *v = pParse->pVdbe; - int i; - for(i=0; i<pEList->nExpr; i++){ - Expr *p = pEList->a[i].pExpr; - const char *zType = columnType(pParse, pTabList, p); - if( zType==0 ) continue; - /* The vdbe must make it's own copy of the column-type, in case the - ** schema is reset before this virtual machine is deleted. - */ - sqlite3VdbeSetColName(v, i+pEList->nExpr, zType, strlen(zType)); - } -} - -/* -** Generate code that will tell the VDBE the names of columns -** in the result set. This information is used to provide the -** azCol[] values in the callback. -*/ -static void generateColumnNames( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* List of tables */ - ExprList *pEList /* Expressions defining the result set */ -){ - Vdbe *v = pParse->pVdbe; - int i, j; - sqlite3 *db = pParse->db; - int fullNames, shortNames; - - /* If this is an EXPLAIN, skip this step */ - if( pParse->explain ){ - return; - } - - assert( v!=0 ); - if( pParse->colNamesSet || v==0 || sqlite3_malloc_failed ) return; - pParse->colNamesSet = 1; - fullNames = (db->flags & SQLITE_FullColNames)!=0; - shortNames = (db->flags & SQLITE_ShortColNames)!=0; - sqlite3VdbeSetNumCols(v, pEList->nExpr); - for(i=0; i<pEList->nExpr; i++){ - Expr *p; - p = pEList->a[i].pExpr; - if( p==0 ) continue; - if( pEList->a[i].zName ){ - char *zName = pEList->a[i].zName; - sqlite3VdbeSetColName(v, i, zName, strlen(zName)); - continue; - } - if( p->op==TK_COLUMN && pTabList ){ - Table *pTab; - char *zCol; - int iCol = p->iColumn; - for(j=0; j<pTabList->nSrc && pTabList->a[j].iCursor!=p->iTable; j++){} - assert( j<pTabList->nSrc ); - pTab = pTabList->a[j].pTab; - if( iCol<0 ) iCol = pTab->iPKey; - assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) ); - if( iCol<0 ){ - zCol = "_ROWID_"; - }else{ - zCol = pTab->aCol[iCol].zName; - } - if( !shortNames && !fullNames && p->span.z && p->span.z[0] ){ - sqlite3VdbeSetColName(v, i, p->span.z, p->span.n); - }else if( fullNames || (!shortNames && pTabList->nSrc>1) ){ - char *zName = 0; - char *zTab; - - zTab = pTabList->a[j].zAlias; - if( fullNames || zTab==0 ) zTab = pTab->zName; - sqlite3SetString(&zName, zTab, ".", zCol, 0); - sqlite3VdbeSetColName(v, i, zName, P3_DYNAMIC); - }else{ - sqlite3VdbeSetColName(v, i, zCol, 0); - } - }else if( p->span.z && p->span.z[0] ){ - sqlite3VdbeSetColName(v, i, p->span.z, p->span.n); - /* sqlite3VdbeCompressSpace(v, addr); */ - }else{ - char zName[30]; - assert( p->op!=TK_COLUMN || pTabList==0 ); - sprintf(zName, "column%d", i+1); - sqlite3VdbeSetColName(v, i, zName, 0); - } - } - generateColumnTypes(pParse, pTabList, pEList); -} - -/* -** Name of the connection operator, used for error messages. -*/ -static const char *selectOpName(int id){ - char *z; - switch( id ){ - case TK_ALL: z = "UNION ALL"; break; - case TK_INTERSECT: z = "INTERSECT"; break; - case TK_EXCEPT: z = "EXCEPT"; break; - default: z = "UNION"; break; - } - return z; -} - -/* -** Forward declaration -*/ -static int fillInColumnList(Parse*, Select*); - -/* -** Given a SELECT statement, generate a Table structure that describes -** the result set of that SELECT. -*/ -Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ - Table *pTab; - int i, j; - ExprList *pEList; - Column *aCol, *pCol; - - if( fillInColumnList(pParse, pSelect) ){ - return 0; - } - pTab = sqliteMalloc( sizeof(Table) ); - if( pTab==0 ){ - return 0; - } - pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0; - pEList = pSelect->pEList; - pTab->nCol = pEList->nExpr; - assert( pTab->nCol>0 ); - pTab->aCol = aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol ); - for(i=0, pCol=aCol; i<pTab->nCol; i++, pCol++){ - Expr *pR; - char *zType; - char *zName; - Expr *p = pEList->a[i].pExpr; - assert( p->pRight==0 || p->pRight->token.z==0 || p->pRight->token.z[0]!=0 ); - if( (zName = pEList->a[i].zName)!=0 ){ - zName = sqliteStrDup(zName); - }else if( p->op==TK_DOT - && (pR=p->pRight)!=0 && pR->token.z && pR->token.z[0] ){ - int cnt; - zName = sqlite3MPrintf("%T", &pR->token); - for(j=cnt=0; j<i; j++){ - if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ - sqliteFree(zName); - zName = sqlite3MPrintf("%T_%d", &pR->token, ++cnt); - j = -1; - } - } - }else if( p->span.z && p->span.z[0] ){ - zName = sqlite3MPrintf("%T", &p->span); - }else{ - zName = sqlite3MPrintf("column%d", i+1); - } - sqlite3Dequote(zName); - pCol->zName = zName; - - zType = sqliteStrDup(columnType(pParse, pSelect->pSrc ,p)); - pCol->zType = zType; - pCol->affinity = SQLITE_AFF_NUMERIC; - if( zType ){ - pCol->affinity = sqlite3AffinityType(zType, strlen(zType)); - } - pCol->pColl = sqlite3ExprCollSeq(pParse, p); - if( !pCol->pColl ){ - pCol->pColl = pParse->db->pDfltColl; - } - } - pTab->iPKey = -1; - return pTab; -} - -/* -** For the given SELECT statement, do three things. -** -** (1) Fill in the pTabList->a[].pTab fields in the SrcList that -** defines the set of tables that should be scanned. For views, -** fill pTabList->a[].pSelect with a copy of the SELECT statement -** that implements the view. A copy is made of the view's SELECT -** statement so that we can freely modify or delete that statement -** without worrying about messing up the presistent representation -** of the view. -** -** (2) Add terms to the WHERE clause to accomodate the NATURAL keyword -** on joins and the ON and USING clause of joins. -** -** (3) Scan the list of columns in the result set (pEList) looking -** for instances of the "*" operator or the TABLE.* operator. -** If found, expand each "*" to be every column in every table -** and TABLE.* to be every column in TABLE. -** -** Return 0 on success. If there are problems, leave an error message -** in pParse and return non-zero. -*/ -static int fillInColumnList(Parse *pParse, Select *p){ - int i, j, k, rc; - SrcList *pTabList; - ExprList *pEList; - Table *pTab; - struct SrcList_item *pFrom; - - if( p==0 || p->pSrc==0 ) return 1; - pTabList = p->pSrc; - pEList = p->pEList; - - /* Look up every table in the table list. - */ - for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ - if( pFrom->pTab ){ - /* This routine has run before! No need to continue */ - return 0; - } - if( pFrom->zName==0 ){ - /* A sub-query in the FROM clause of a SELECT */ - assert( pFrom->pSelect!=0 ); - if( pFrom->zAlias==0 ){ - pFrom->zAlias = - sqlite3MPrintf("sqlite_subquery_%p_", (void*)pFrom->pSelect); - } - pFrom->pTab = pTab = - sqlite3ResultSetOfSelect(pParse, pFrom->zAlias, pFrom->pSelect); - if( pTab==0 ){ - return 1; - } - /* The isTransient flag indicates that the Table structure has been - ** dynamically allocated and may be freed at any time. In other words, - ** pTab is not pointing to a persistent table structure that defines - ** part of the schema. */ - pTab->isTransient = 1; - }else{ - /* An ordinary table or view name in the FROM clause */ - pFrom->pTab = pTab = - sqlite3LocateTable(pParse,pFrom->zName,pFrom->zDatabase); - if( pTab==0 ){ - return 1; - } - if( pTab->pSelect ){ - /* We reach here if the named table is a really a view */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - return 1; - } - /* If pFrom->pSelect!=0 it means we are dealing with a - ** view within a view. The SELECT structure has already been - ** copied by the outer view so we can skip the copy step here - ** in the inner view. - */ - if( pFrom->pSelect==0 ){ - pFrom->pSelect = sqlite3SelectDup(pTab->pSelect); - } - } - } - } - - /* Process NATURAL keywords, and ON and USING clauses of joins. - */ - if( sqliteProcessJoin(pParse, p) ) return 1; - - /* For every "*" that occurs in the column list, insert the names of - ** all columns in all tables. And for every TABLE.* insert the names - ** of all columns in TABLE. The parser inserted a special expression - ** with the TK_ALL operator for each "*" that it found in the column list. - ** The following code just has to locate the TK_ALL expressions and expand - ** each one to the list of all columns in all tables. - ** - ** The first loop just checks to see if there are any "*" operators - ** that need expanding. - */ - for(k=0; k<pEList->nExpr; k++){ - Expr *pE = pEList->a[k].pExpr; - if( pE->op==TK_ALL ) break; - if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL - && pE->pLeft && pE->pLeft->op==TK_ID ) break; - } - rc = 0; - if( k<pEList->nExpr ){ - /* - ** If we get here it means the result set contains one or more "*" - ** operators that need to be expanded. Loop through each expression - ** in the result set and expand them one by one. - */ - struct ExprList_item *a = pEList->a; - ExprList *pNew = 0; - for(k=0; k<pEList->nExpr; k++){ - Expr *pE = a[k].pExpr; - if( pE->op!=TK_ALL && - (pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){ - /* This particular expression does not need to be expanded. - */ - pNew = sqlite3ExprListAppend(pNew, a[k].pExpr, 0); - pNew->a[pNew->nExpr-1].zName = a[k].zName; - a[k].pExpr = 0; - a[k].zName = 0; - }else{ - /* This expression is a "*" or a "TABLE.*" and needs to be - ** expanded. */ - int tableSeen = 0; /* Set to 1 when TABLE matches */ - char *zTName; /* text of name of TABLE */ - if( pE->op==TK_DOT && pE->pLeft ){ - zTName = sqlite3NameFromToken(&pE->pLeft->token); - }else{ - zTName = 0; - } - for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - char *zTabName = pFrom->zAlias; - if( zTabName==0 || zTabName[0]==0 ){ - zTabName = pTab->zName; - } - if( zTName && (zTabName==0 || zTabName[0]==0 || - sqlite3StrICmp(zTName, zTabName)!=0) ){ - continue; - } - tableSeen = 1; - for(j=0; j<pTab->nCol; j++){ - Expr *pExpr, *pLeft, *pRight; - char *zName = pTab->aCol[j].zName; - - if( i>0 ){ - struct SrcList_item *pLeft = &pTabList->a[i-1]; - if( (pLeft->jointype & JT_NATURAL)!=0 && - columnIndex(pLeft->pTab, zName)>=0 ){ - /* In a NATURAL join, omit the join columns from the - ** table on the right */ - continue; - } - if( sqlite3IdListIndex(pLeft->pUsing, zName)>=0 ){ - /* In a join with a USING clause, omit columns in the - ** using clause from the table on the right. */ - continue; - } - } - pRight = sqlite3Expr(TK_ID, 0, 0, 0); - if( pRight==0 ) break; - setToken(&pRight->token, zName); - if( zTabName && pTabList->nSrc>1 ){ - pLeft = sqlite3Expr(TK_ID, 0, 0, 0); - pExpr = sqlite3Expr(TK_DOT, pLeft, pRight, 0); - if( pExpr==0 ) break; - setToken(&pLeft->token, zTabName); - setToken(&pExpr->span, sqlite3MPrintf("%s.%s", zTabName, zName)); - pExpr->span.dyn = 1; - pExpr->token.z = 0; - pExpr->token.n = 0; - pExpr->token.dyn = 0; - }else{ - pExpr = pRight; - pExpr->span = pExpr->token; - } - pNew = sqlite3ExprListAppend(pNew, pExpr, 0); - } - } - if( !tableSeen ){ - if( zTName ){ - sqlite3ErrorMsg(pParse, "no such table: %s", zTName); - }else{ - sqlite3ErrorMsg(pParse, "no tables specified"); - } - rc = 1; - } - sqliteFree(zTName); - } - } - sqlite3ExprListDelete(pEList); - p->pEList = pNew; - } - return rc; -} - -/* -** This routine recursively unlinks the Select.pSrc.a[].pTab pointers -** in a select structure. It just sets the pointers to NULL. This -** routine is recursive in the sense that if the Select.pSrc.a[].pSelect -** pointer is not NULL, this routine is called recursively on that pointer. -** -** This routine is called on the Select structure that defines a -** VIEW in order to undo any bindings to tables. This is necessary -** because those tables might be DROPed by a subsequent SQL command. -** If the bindings are not removed, then the Select.pSrc->a[].pTab field -** will be left pointing to a deallocated Table structure after the -** DROP and a coredump will occur the next time the VIEW is used. -*/ -void sqlite3SelectUnbind(Select *p){ - int i; - SrcList *pSrc = p->pSrc; - struct SrcList_item *pItem; - Table *pTab; - if( p==0 ) return; - for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){ - if( (pTab = pItem->pTab)!=0 ){ - if( pTab->isTransient ){ - sqlite3DeleteTable(0, pTab); - } - pItem->pTab = 0; - if( pItem->pSelect ){ - sqlite3SelectUnbind(pItem->pSelect); - } - } - } -} - -/* -** This routine associates entries in an ORDER BY expression list with -** columns in a result. For each ORDER BY expression, the opcode of -** the top-level node is changed to TK_COLUMN and the iColumn value of -** the top-level node is filled in with column number and the iTable -** value of the top-level node is filled with iTable parameter. -** -** If there are prior SELECT clauses, they are processed first. A match -** in an earlier SELECT takes precedence over a later SELECT. -** -** Any entry that does not match is flagged as an error. The number -** of errors is returned. -*/ -static int matchOrderbyToColumn( - Parse *pParse, /* A place to leave error messages */ - Select *pSelect, /* Match to result columns of this SELECT */ - ExprList *pOrderBy, /* The ORDER BY values to match against columns */ - int iTable, /* Insert this value in iTable */ - int mustComplete /* If TRUE all ORDER BYs must match */ -){ - int nErr = 0; - int i, j; - ExprList *pEList; - - if( pSelect==0 || pOrderBy==0 ) return 1; - if( mustComplete ){ - for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; } - } - if( fillInColumnList(pParse, pSelect) ){ - return 1; - } - if( pSelect->pPrior ){ - if( matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0) ){ - return 1; - } - } - pEList = pSelect->pEList; - for(i=0; i<pOrderBy->nExpr; i++){ - Expr *pE = pOrderBy->a[i].pExpr; - int iCol = -1; - if( pOrderBy->a[i].done ) continue; - if( sqlite3ExprIsInteger(pE, &iCol) ){ - if( iCol<=0 || iCol>pEList->nExpr ){ - sqlite3ErrorMsg(pParse, - "ORDER BY position %d should be between 1 and %d", - iCol, pEList->nExpr); - nErr++; - break; - } - if( !mustComplete ) continue; - iCol--; - } - for(j=0; iCol<0 && j<pEList->nExpr; j++){ - if( pEList->a[j].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){ - char *zName, *zLabel; - zName = pEList->a[j].zName; - zLabel = sqlite3NameFromToken(&pE->token); - assert( zLabel!=0 ); - if( sqlite3StrICmp(zName, zLabel)==0 ){ - iCol = j; - } - sqliteFree(zLabel); - } - if( iCol<0 && sqlite3ExprCompare(pE, pEList->a[j].pExpr) ){ - iCol = j; - } - } - if( iCol>=0 ){ - pE->op = TK_COLUMN; - pE->iColumn = iCol; - pE->iTable = iTable; - pOrderBy->a[i].done = 1; - } - if( iCol<0 && mustComplete ){ - sqlite3ErrorMsg(pParse, - "ORDER BY term number %d does not match any result column", i+1); - nErr++; - break; - } - } - return nErr; -} - -/* -** Get a VDBE for the given parser context. Create a new one if necessary. -** If an error occurs, return NULL and leave a message in pParse. -*/ -Vdbe *sqlite3GetVdbe(Parse *pParse){ - Vdbe *v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); - } - return v; -} - -/* -** Compute the iLimit and iOffset fields of the SELECT based on the -** nLimit and nOffset fields. nLimit and nOffset hold the integers -** that appear in the original SQL statement after the LIMIT and OFFSET -** keywords. Or that hold -1 and 0 if those keywords are omitted. -** iLimit and iOffset are the integer memory register numbers for -** counters used to compute the limit and offset. If there is no -** limit and/or offset, then iLimit and iOffset are negative. -** -** This routine changes the values if iLimit and iOffset only if -** a limit or offset is defined by nLimit and nOffset. iLimit and -** iOffset should have been preset to appropriate default values -** (usually but not always -1) prior to calling this routine. -** Only if nLimit>=0 or nOffset>0 do the limit registers get -** redefined. The UNION ALL operator uses this property to force -** the reuse of the same limit and offset registers across multiple -** SELECT statements. -*/ -static void computeLimitRegisters(Parse *pParse, Select *p){ - /* - ** If the comparison is p->nLimit>0 then "LIMIT 0" shows - ** all rows. It is the same as no limit. If the comparision is - ** p->nLimit>=0 then "LIMIT 0" show no rows at all. - ** "LIMIT -1" always shows all rows. There is some - ** contraversy about what the correct behavior should be. - ** The current implementation interprets "LIMIT 0" to mean - ** no rows. - */ - if( p->nLimit>=0 ){ - int iMem = pParse->nMem++; - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - sqlite3VdbeAddOp(v, OP_Integer, -p->nLimit, 0); - sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1); - VdbeComment((v, "# LIMIT counter")); - p->iLimit = iMem; - } - if( p->nOffset>0 ){ - int iMem = pParse->nMem++; - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - sqlite3VdbeAddOp(v, OP_Integer, -p->nOffset, 0); - sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1); - VdbeComment((v, "# OFFSET counter")); - p->iOffset = iMem; - } -} - -/* -** Generate VDBE instructions that will open a transient table that -** will be used for an index or to store keyed results for a compound -** select. In other words, open a transient table that needs a -** KeyInfo structure. The number of columns in the KeyInfo is determined -** by the result set of the SELECT statement in the second argument. -** -** Specifically, this routine is called to open an index table for -** DISTINCT, UNION, INTERSECT and EXCEPT select statements (but not -** UNION ALL). -** -** Make the new table a KeyAsData table if keyAsData is true. -** -** The value returned is the address of the OP_OpenTemp instruction. -*/ -static int openTempIndex(Parse *pParse, Select *p, int iTab, int keyAsData){ - KeyInfo *pKeyInfo; - int nColumn; - sqlite3 *db = pParse->db; - int i; - Vdbe *v = pParse->pVdbe; - int addr; - - if( fillInColumnList(pParse, p) ){ - return 0; - } - nColumn = p->pEList->nExpr; - pKeyInfo = sqliteMalloc( sizeof(*pKeyInfo)+nColumn*sizeof(CollSeq*) ); - if( pKeyInfo==0 ) return 0; - pKeyInfo->enc = db->enc; - pKeyInfo->nField = nColumn; - for(i=0; i<nColumn; i++){ - pKeyInfo->aColl[i] = sqlite3ExprCollSeq(pParse, p->pEList->a[i].pExpr); - if( !pKeyInfo->aColl[i] ){ - pKeyInfo->aColl[i] = db->pDfltColl; - } - } - addr = sqlite3VdbeOp3(v, OP_OpenTemp, iTab, 0, - (char*)pKeyInfo, P3_KEYINFO_HANDOFF); - if( keyAsData ){ - sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1); - } - return addr; -} - -/* -** Add the address "addr" to the set of all OpenTemp opcode addresses -** that are being accumulated in p->ppOpenTemp. -*/ -static int multiSelectOpenTempAddr(Select *p, int addr){ - IdList *pList = *p->ppOpenTemp = sqlite3IdListAppend(*p->ppOpenTemp, 0); - if( pList==0 ){ - return SQLITE_NOMEM; - } - pList->a[pList->nId-1].idx = addr; - return SQLITE_OK; -} - -/* -** Return the appropriate collating sequence for the iCol-th column of -** the result set for the compound-select statement "p". Return NULL if -** the column has no default collating sequence. -** -** The collating sequence for the compound select is taken from the -** left-most term of the select that has a collating sequence. -*/ -static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ - CollSeq *pRet; - if( p->pPrior ){ - pRet = multiSelectCollSeq(pParse, p->pPrior, iCol); - }else{ - pRet = 0; - } - if( pRet==0 ){ - pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr); - } - return pRet; -} - -/* -** This routine is called to process a query that is really the union -** or intersection of two or more separate queries. -** -** "p" points to the right-most of the two queries. the query on the -** left is p->pPrior. The left query could also be a compound query -** in which case this routine will be called recursively. -** -** The results of the total query are to be written into a destination -** of type eDest with parameter iParm. -** -** Example 1: Consider a three-way compound SQL statement. -** -** SELECT a FROM t1 UNION SELECT b FROM t2 UNION SELECT c FROM t3 -** -** This statement is parsed up as follows: -** -** SELECT c FROM t3 -** | -** `-----> SELECT b FROM t2 -** | -** `------> SELECT a FROM t1 -** -** The arrows in the diagram above represent the Select.pPrior pointer. -** So if this routine is called with p equal to the t3 query, then -** pPrior will be the t2 query. p->op will be TK_UNION in this case. -** -** Notice that because of the way SQLite parses compound SELECTs, the -** individual selects always group from left to right. -*/ -static int multiSelect( - Parse *pParse, /* Parsing context */ - Select *p, /* The right-most of SELECTs to be coded */ - int eDest, /* \___ Store query results as specified */ - int iParm, /* / by these two parameters. */ - char *aff /* If eDest is SRT_Union, the affinity string */ -){ - int rc = SQLITE_OK; /* Success code from a subroutine */ - Select *pPrior; /* Another SELECT immediately to our left */ - Vdbe *v; /* Generate code to this VDBE */ - IdList *pOpenTemp = 0;/* OP_OpenTemp opcodes that need a KeyInfo */ - int aAddr[5]; /* Addresses of SetNumColumns operators */ - int nAddr = 0; /* Number used */ - int nCol; /* Number of columns in the result set */ - - /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only - ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. - */ - if( p==0 || p->pPrior==0 ){ - rc = 1; - goto multi_select_end; - } - pPrior = p->pPrior; - if( pPrior->pOrderBy ){ - sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before", - selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } - if( pPrior->nLimit>=0 || pPrior->nOffset>0 ){ - sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before", - selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } - - /* Make sure we have a valid query engine. If not, create a new one. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ){ - rc = 1; - goto multi_select_end; - } - - /* If *p this is the right-most select statement, then initialize - ** p->ppOpenTemp to point to pOpenTemp. If *p is not the right most - ** statement then p->ppOpenTemp will have already been initialized - ** by a prior call to this same procedure. Pass along the pOpenTemp - ** pointer to pPrior, the next statement to our left. - */ - if( p->ppOpenTemp==0 ){ - p->ppOpenTemp = &pOpenTemp; - } - pPrior->ppOpenTemp = p->ppOpenTemp; - - /* Create the destination temporary table if necessary - */ - if( eDest==SRT_TempTable ){ - assert( p->pEList ); - sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0); - assert( nAddr==0 ); - aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 0); - eDest = SRT_Table; - } - - /* Generate code for the left and right SELECT statements. - */ - switch( p->op ){ - case TK_ALL: { - if( p->pOrderBy==0 ){ - pPrior->nLimit = p->nLimit; - pPrior->nOffset = p->nOffset; - rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff); - if( rc ){ - goto multi_select_end; - } - p->pPrior = 0; - p->iLimit = pPrior->iLimit; - p->iOffset = pPrior->iOffset; - p->nLimit = -1; - p->nOffset = 0; - rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff); - p->pPrior = pPrior; - if( rc ){ - goto multi_select_end; - } - break; - } - /* For UNION ALL ... ORDER BY fall through to the next case */ - } - case TK_EXCEPT: - case TK_UNION: { - int unionTab; /* Cursor number of the temporary table holding result */ - int op = 0; /* One of the SRT_ operations to apply to self */ - int priorOp; /* The SRT_ operation to apply to prior selects */ - int nLimit, nOffset; /* Saved values of p->nLimit and p->nOffset */ - ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */ - int addr; - - priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union; - if( eDest==priorOp && p->pOrderBy==0 && p->nLimit<0 && p->nOffset==0 ){ - /* We can reuse a temporary table generated by a SELECT to our - ** right. - */ - unionTab = iParm; - }else{ - /* We will need to create our own temporary table to hold the - ** intermediate results. - */ - unionTab = pParse->nTab++; - if( p->pOrderBy - && matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){ - rc = 1; - goto multi_select_end; - } - addr = sqlite3VdbeAddOp(v, OP_OpenTemp, unionTab, 0); - if( p->op!=TK_ALL ){ - rc = multiSelectOpenTempAddr(p, addr); - if( rc!=SQLITE_OK ){ - goto multi_select_end; - } - sqlite3VdbeAddOp(v, OP_KeyAsData, unionTab, 1); - } - assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) ); - aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, unionTab, 0); - assert( p->pEList ); - } - - /* Code the SELECT statements to our left - */ - rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff); - if( rc ){ - goto multi_select_end; - } - - /* Code the current SELECT statement - */ - switch( p->op ){ - case TK_EXCEPT: op = SRT_Except; break; - case TK_UNION: op = SRT_Union; break; - case TK_ALL: op = SRT_Table; break; - } - p->pPrior = 0; - pOrderBy = p->pOrderBy; - p->pOrderBy = 0; - nLimit = p->nLimit; - p->nLimit = -1; - nOffset = p->nOffset; - p->nOffset = 0; - rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff); - p->pPrior = pPrior; - p->pOrderBy = pOrderBy; - p->nLimit = nLimit; - p->nOffset = nOffset; - if( rc ){ - goto multi_select_end; - } - - - /* Convert the data in the temporary table into whatever form - ** it is that we currently need. - */ - if( eDest!=priorOp || unionTab!=iParm ){ - int iCont, iBreak, iStart; - assert( p->pEList ); - if( eDest==SRT_Callback ){ - generateColumnNames(pParse, 0, p->pEList); - } - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Rewind, unionTab, iBreak); - computeLimitRegisters(pParse, p); - iStart = sqlite3VdbeCurrentAddr(v); - rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr, - p->pOrderBy, -1, eDest, iParm, - iCont, iBreak, 0); - if( rc ){ - rc = 1; - goto multi_select_end; - } - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp(v, OP_Next, unionTab, iStart); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp(v, OP_Close, unionTab, 0); - } - break; - } - case TK_INTERSECT: { - int tab1, tab2; - int iCont, iBreak, iStart; - int nLimit, nOffset; - int addr; - - /* INTERSECT is different from the others since it requires - ** two temporary tables. Hence it has its own case. Begin - ** by allocating the tables we will need. - */ - tab1 = pParse->nTab++; - tab2 = pParse->nTab++; - if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){ - rc = 1; - goto multi_select_end; - } - - addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab1, 0); - rc = multiSelectOpenTempAddr(p, addr); - if( rc!=SQLITE_OK ){ - goto multi_select_end; - } - sqlite3VdbeAddOp(v, OP_KeyAsData, tab1, 1); - assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) ); - aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab1, 0); - assert( p->pEList ); - - /* Code the SELECTs to our left into temporary table "tab1". - */ - rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff); - if( rc ){ - goto multi_select_end; - } - - /* Code the current SELECT into temporary table "tab2" - */ - addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab2, 0); - rc = multiSelectOpenTempAddr(p, addr); - if( rc!=SQLITE_OK ){ - goto multi_select_end; - } - sqlite3VdbeAddOp(v, OP_KeyAsData, tab2, 1); - assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) ); - aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0); - p->pPrior = 0; - nLimit = p->nLimit; - p->nLimit = -1; - nOffset = p->nOffset; - p->nOffset = 0; - rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff); - p->pPrior = pPrior; - p->nLimit = nLimit; - p->nOffset = nOffset; - if( rc ){ - goto multi_select_end; - } - - /* Generate code to take the intersection of the two temporary - ** tables. - */ - assert( p->pEList ); - if( eDest==SRT_Callback ){ - generateColumnNames(pParse, 0, p->pEList); - } - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Rewind, tab1, iBreak); - computeLimitRegisters(pParse, p); - iStart = sqlite3VdbeAddOp(v, OP_FullKey, tab1, 0); - sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont); - rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, - p->pOrderBy, -1, eDest, iParm, - iCont, iBreak, 0); - if( rc ){ - rc = 1; - goto multi_select_end; - } - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp(v, OP_Next, tab1, iStart); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp(v, OP_Close, tab2, 0); - sqlite3VdbeAddOp(v, OP_Close, tab1, 0); - break; - } - } - - /* Make sure all SELECTs in the statement have the same number of elements - ** in their result sets. - */ - assert( p->pEList && pPrior->pEList ); - if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } - - /* Set the number of columns in temporary tables - */ - nCol = p->pEList->nExpr; - while( nAddr>0 ){ - nAddr--; - sqlite3VdbeChangeP2(v, aAddr[nAddr], nCol); - } - - /* Compute collating sequences used by either the ORDER BY clause or - ** by any temporary tables needed to implement the compound select. - ** Attach the KeyInfo structure to all temporary tables. Invoke the - ** ORDER BY processing if there is an ORDER BY clause. - ** - ** This section is run by the right-most SELECT statement only. - ** SELECT statements to the left always skip this part. The right-most - ** SELECT might also skip this part if it has no ORDER BY clause and - ** no temp tables are required. - */ - if( p->pOrderBy || (pOpenTemp && pOpenTemp->nId>0) ){ - int i; /* Loop counter */ - KeyInfo *pKeyInfo; /* Collating sequence for the result set */ - - assert( p->ppOpenTemp == &pOpenTemp ); - pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nCol*sizeof(CollSeq*)); - if( !pKeyInfo ){ - rc = SQLITE_NOMEM; - goto multi_select_end; - } - - pKeyInfo->enc = pParse->db->enc; - pKeyInfo->nField = nCol; - - for(i=0; i<nCol; i++){ - pKeyInfo->aColl[i] = multiSelectCollSeq(pParse, p, i); - if( !pKeyInfo->aColl[i] ){ - pKeyInfo->aColl[i] = pParse->db->pDfltColl; - } - } - - for(i=0; pOpenTemp && i<pOpenTemp->nId; i++){ - int p3type = (i==0?P3_KEYINFO_HANDOFF:P3_KEYINFO); - int addr = pOpenTemp->a[i].idx; - sqlite3VdbeChangeP3(v, addr, (char *)pKeyInfo, p3type); - } - - if( p->pOrderBy ){ - struct ExprList_item *pOrderByTerm = p->pOrderBy->a; - for(i=0; i<p->pOrderBy->nExpr; i++, pOrderByTerm++){ - Expr *pExpr = pOrderByTerm->pExpr; - char *zName = pOrderByTerm->zName; - assert( pExpr->op==TK_COLUMN && pExpr->iColumn<nCol ); - assert( !pExpr->pColl ); - if( zName ){ - pExpr->pColl = sqlite3LocateCollSeq(pParse, zName, -1); - }else{ - pExpr->pColl = pKeyInfo->aColl[pExpr->iColumn]; - } - } - generateSortTail(pParse, p, v, p->pEList->nExpr, eDest, iParm); - } - - if( !pOpenTemp ){ - /* This happens for UNION ALL ... ORDER BY */ - sqliteFree(pKeyInfo); - } - } - -multi_select_end: - if( pOpenTemp ){ - sqlite3IdListDelete(pOpenTemp); - } - p->ppOpenTemp = 0; - return rc; -} - -/* -** Scan through the expression pExpr. Replace every reference to -** a column in table number iTable with a copy of the iColumn-th -** entry in pEList. (But leave references to the ROWID column -** unchanged.) -** -** This routine is part of the flattening procedure. A subquery -** whose result set is defined by pEList appears as entry in the -** FROM clause of a SELECT such that the VDBE cursor assigned to that -** FORM clause entry is iTable. This routine make the necessary -** changes to pExpr so that it refers directly to the source table -** of the subquery rather the result set of the subquery. -*/ -static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */ -static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){ - if( pExpr==0 ) return; - if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){ - if( pExpr->iColumn<0 ){ - pExpr->op = TK_NULL; - }else{ - Expr *pNew; - assert( pEList!=0 && pExpr->iColumn<pEList->nExpr ); - assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 ); - pNew = pEList->a[pExpr->iColumn].pExpr; - assert( pNew!=0 ); - pExpr->op = pNew->op; - assert( pExpr->pLeft==0 ); - pExpr->pLeft = sqlite3ExprDup(pNew->pLeft); - assert( pExpr->pRight==0 ); - pExpr->pRight = sqlite3ExprDup(pNew->pRight); - assert( pExpr->pList==0 ); - pExpr->pList = sqlite3ExprListDup(pNew->pList); - pExpr->iTable = pNew->iTable; - pExpr->iColumn = pNew->iColumn; - pExpr->iAgg = pNew->iAgg; - sqlite3TokenCopy(&pExpr->token, &pNew->token); - sqlite3TokenCopy(&pExpr->span, &pNew->span); - } - }else{ - substExpr(pExpr->pLeft, iTable, pEList); - substExpr(pExpr->pRight, iTable, pEList); - substExprList(pExpr->pList, iTable, pEList); - } -} -static void -substExprList(ExprList *pList, int iTable, ExprList *pEList){ - int i; - if( pList==0 ) return; - for(i=0; i<pList->nExpr; i++){ - substExpr(pList->a[i].pExpr, iTable, pEList); - } -} - -/* -** This routine attempts to flatten subqueries in order to speed -** execution. It returns 1 if it makes changes and 0 if no flattening -** occurs. -** -** To understand the concept of flattening, consider the following -** query: -** -** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5 -** -** The default way of implementing this query is to execute the -** subquery first and store the results in a temporary table, then -** run the outer query on that temporary table. This requires two -** passes over the data. Furthermore, because the temporary table -** has no indices, the WHERE clause on the outer query cannot be -** optimized. -** -** This routine attempts to rewrite queries such as the above into -** a single flat select, like this: -** -** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5 -** -** The code generated for this simpification gives the same result -** but only has to scan the data once. And because indices might -** exist on the table t1, a complete scan of the data might be -** avoided. -** -** Flattening is only attempted if all of the following are true: -** -** (1) The subquery and the outer query do not both use aggregates. -** -** (2) The subquery is not an aggregate or the outer query is not a join. -** -** (3) The subquery is not the right operand of a left outer join, or -** the subquery is not itself a join. (Ticket #306) -** -** (4) The subquery is not DISTINCT or the outer query is not a join. -** -** (5) The subquery is not DISTINCT or the outer query does not use -** aggregates. -** -** (6) The subquery does not use aggregates or the outer query is not -** DISTINCT. -** -** (7) The subquery has a FROM clause. -** -** (8) The subquery does not use LIMIT or the outer query is not a join. -** -** (9) The subquery does not use LIMIT or the outer query does not use -** aggregates. -** -** (10) The subquery does not use aggregates or the outer query does not -** use LIMIT. -** -** (11) The subquery and the outer query do not both have ORDER BY clauses. -** -** (12) The subquery is not the right term of a LEFT OUTER JOIN or the -** subquery has no WHERE clause. (added by ticket #350) -** -** In this routine, the "p" parameter is a pointer to the outer query. -** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query -** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. -** -** If flattening is not attempted, this routine is a no-op and returns 0. -** If flattening is attempted this routine returns 1. -** -** All of the expression analysis must occur on both the outer query and -** the subquery before this routine runs. -*/ -static int flattenSubquery( - Parse *pParse, /* The parsing context */ - Select *p, /* The parent or outer SELECT statement */ - int iFrom, /* Index in p->pSrc->a[] of the inner subquery */ - int isAgg, /* True if outer SELECT uses aggregate functions */ - int subqueryIsAgg /* True if the subquery uses aggregate functions */ -){ - Select *pSub; /* The inner query or "subquery" */ - SrcList *pSrc; /* The FROM clause of the outer query */ - SrcList *pSubSrc; /* The FROM clause of the subquery */ - ExprList *pList; /* The result set of the outer query */ - int iParent; /* VDBE cursor number of the pSub result set temp table */ - int i; /* Loop counter */ - Expr *pWhere; /* The WHERE clause */ - struct SrcList_item *pSubitem; /* The subquery */ - - /* Check to see if flattening is permitted. Return 0 if not. - */ - if( p==0 ) return 0; - pSrc = p->pSrc; - assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc ); - pSubitem = &pSrc->a[iFrom]; - pSub = pSubitem->pSelect; - assert( pSub!=0 ); - if( isAgg && subqueryIsAgg ) return 0; - if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; - pSubSrc = pSub->pSrc; - assert( pSubSrc ); - if( pSubSrc->nSrc==0 ) return 0; - if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){ - return 0; - } - if( (p->isDistinct || p->nLimit>=0) && subqueryIsAgg ) return 0; - if( p->pOrderBy && pSub->pOrderBy ) return 0; - - /* Restriction 3: If the subquery is a join, make sure the subquery is - ** not used as the right operand of an outer join. Examples of why this - ** is not allowed: - ** - ** t1 LEFT OUTER JOIN (t2 JOIN t3) - ** - ** If we flatten the above, we would get - ** - ** (t1 LEFT OUTER JOIN t2) JOIN t3 - ** - ** which is not at all the same thing. - */ - if( pSubSrc->nSrc>1 && iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 ){ - return 0; - } - - /* Restriction 12: If the subquery is the right operand of a left outer - ** join, make sure the subquery has no WHERE clause. - ** An examples of why this is not allowed: - ** - ** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0) - ** - ** If we flatten the above, we would get - ** - ** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0 - ** - ** But the t2.x>0 test will always fail on a NULL row of t2, which - ** effectively converts the OUTER JOIN into an INNER JOIN. - */ - if( iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 - && pSub->pWhere!=0 ){ - return 0; - } - - /* If we reach this point, it means flattening is permitted for the - ** iFrom-th entry of the FROM clause in the outer query. - */ - - /* Move all of the FROM elements of the subquery into the - ** the FROM clause of the outer query. Before doing this, remember - ** the cursor number for the original outer query FROM element in - ** iParent. The iParent cursor will never be used. Subsequent code - ** will scan expressions looking for iParent references and replace - ** those references with expressions that resolve to the subquery FROM - ** elements we are now copying in. - */ - iParent = pSubitem->iCursor; - { - int nSubSrc = pSubSrc->nSrc; - int jointype = pSubitem->jointype; - Table *pTab = pSubitem->pTab; - - if( pTab && pTab->isTransient ){ - sqlite3DeleteTable(0, pSubitem->pTab); - } - sqliteFree(pSubitem->zDatabase); - sqliteFree(pSubitem->zName); - sqliteFree(pSubitem->zAlias); - if( nSubSrc>1 ){ - int extra = nSubSrc - 1; - for(i=1; i<nSubSrc; i++){ - pSrc = sqlite3SrcListAppend(pSrc, 0, 0); - } - p->pSrc = pSrc; - for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){ - pSrc->a[i] = pSrc->a[i-extra]; - } - } - for(i=0; i<nSubSrc; i++){ - pSrc->a[i+iFrom] = pSubSrc->a[i]; - memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); - } - pSrc->a[iFrom+nSubSrc-1].jointype = jointype; - } - - /* Now begin substituting subquery result set expressions for - ** references to the iParent in the outer query. - ** - ** Example: - ** - ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; - ** \ \_____________ subquery __________/ / - ** \_____________________ outer query ______________________________/ - ** - ** We look at every expression in the outer query and every place we see - ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". - */ - substExprList(p->pEList, iParent, pSub->pEList); - pList = p->pEList; - for(i=0; i<pList->nExpr; i++){ - Expr *pExpr; - if( pList->a[i].zName==0 && (pExpr = pList->a[i].pExpr)->span.z!=0 ){ - pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n); - } - } - if( isAgg ){ - substExprList(p->pGroupBy, iParent, pSub->pEList); - substExpr(p->pHaving, iParent, pSub->pEList); - } - if( pSub->pOrderBy ){ - assert( p->pOrderBy==0 ); - p->pOrderBy = pSub->pOrderBy; - pSub->pOrderBy = 0; - }else if( p->pOrderBy ){ - substExprList(p->pOrderBy, iParent, pSub->pEList); - } - if( pSub->pWhere ){ - pWhere = sqlite3ExprDup(pSub->pWhere); - }else{ - pWhere = 0; - } - if( subqueryIsAgg ){ - assert( p->pHaving==0 ); - p->pHaving = p->pWhere; - p->pWhere = pWhere; - substExpr(p->pHaving, iParent, pSub->pEList); - p->pHaving = sqlite3ExprAnd(p->pHaving, sqlite3ExprDup(pSub->pHaving)); - assert( p->pGroupBy==0 ); - p->pGroupBy = sqlite3ExprListDup(pSub->pGroupBy); - }else{ - substExpr(p->pWhere, iParent, pSub->pEList); - p->pWhere = sqlite3ExprAnd(p->pWhere, pWhere); - } - - /* The flattened query is distinct if either the inner or the - ** outer query is distinct. - */ - p->isDistinct = p->isDistinct || pSub->isDistinct; - - /* Transfer the limit expression from the subquery to the outer - ** query. - */ - if( pSub->nLimit>=0 ){ - if( p->nLimit<0 ){ - p->nLimit = pSub->nLimit; - }else if( p->nLimit+p->nOffset > pSub->nLimit+pSub->nOffset ){ - p->nLimit = pSub->nLimit + pSub->nOffset - p->nOffset; - } - } - p->nOffset += pSub->nOffset; - - /* Finially, delete what is left of the subquery and return - ** success. - */ - sqlite3SelectDelete(pSub); - return 1; -} - -/* -** Analyze the SELECT statement passed in as an argument to see if it -** is a simple min() or max() query. If it is and this query can be -** satisfied using a single seek to the beginning or end of an index, -** then generate the code for this SELECT and return 1. If this is not a -** simple min() or max() query, then return 0; -** -** A simply min() or max() query looks like this: -** -** SELECT min(a) FROM table; -** SELECT max(a) FROM table; -** -** The query may have only a single table in its FROM argument. There -** can be no GROUP BY or HAVING or WHERE clauses. The result set must -** be the min() or max() of a single column of the table. The column -** in the min() or max() function must be indexed. -** -** The parameters to this routine are the same as for sqlite3Select(). -** See the header comment on that routine for additional information. -*/ -static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ - Expr *pExpr; - int iCol; - Table *pTab; - Index *pIdx; - int base; - Vdbe *v; - int seekOp; - int cont; - ExprList *pEList, *pList, eList; - struct ExprList_item eListItem; - SrcList *pSrc; - - - /* Check to see if this query is a simple min() or max() query. Return - ** zero if it is not. - */ - if( p->pGroupBy || p->pHaving || p->pWhere ) return 0; - pSrc = p->pSrc; - if( pSrc->nSrc!=1 ) return 0; - pEList = p->pEList; - if( pEList->nExpr!=1 ) return 0; - pExpr = pEList->a[0].pExpr; - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - pList = pExpr->pList; - if( pList==0 || pList->nExpr!=1 ) return 0; - if( pExpr->token.n!=3 ) return 0; - if( sqlite3StrNICmp(pExpr->token.z,"min",3)==0 ){ - seekOp = OP_Rewind; - }else if( sqlite3StrNICmp(pExpr->token.z,"max",3)==0 ){ - seekOp = OP_Last; - }else{ - return 0; - } - pExpr = pList->a[0].pExpr; - if( pExpr->op!=TK_COLUMN ) return 0; - iCol = pExpr->iColumn; - pTab = pSrc->a[0].pTab; - - /* If we get to here, it means the query is of the correct form. - ** Check to make sure we have an index and make pIdx point to the - ** appropriate index. If the min() or max() is on an INTEGER PRIMARY - ** key column, no index is necessary so set pIdx to NULL. If no - ** usable index is found, return 0. - */ - if( iCol<0 ){ - pIdx = 0; - }else{ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->nColumn>=1 ); - if( pIdx->aiColumn[0]==iCol && pIdx->keyInfo.aColl[0]==pColl ) break; - } - if( pIdx==0 ) return 0; - } - - /* Identify column types if we will be using the callback. This - ** step is skipped if the output is going to a table or a memory cell. - ** The column names have already been generated in the calling function. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) return 0; - - /* If the output is destined for a temporary table, open that table. - */ - if( eDest==SRT_TempTable ){ - sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 1); - } - - /* Generating code to find the min or the max. Basically all we have - ** to do is find the first or the last entry in the chosen index. If - ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first - ** or last entry in the main table. - */ - sqlite3CodeVerifySchema(pParse, pTab->iDb); - base = pSrc->a[0].iCursor; - computeLimitRegisters(pParse, p); - if( pSrc->a[0].pSelect==0 ){ - sqlite3OpenTableForReading(v, base, pTab); - } - cont = sqlite3VdbeMakeLabel(v); - if( pIdx==0 ){ - sqlite3VdbeAddOp(v, seekOp, base, 0); - }else{ - sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0); - sqlite3VdbeOp3(v, OP_OpenRead, base+1, pIdx->tnum, - (char*)&pIdx->keyInfo, P3_KEYINFO); - if( seekOp==OP_Rewind ){ - sqlite3VdbeAddOp(v, OP_String, 0, 0); - sqlite3VdbeAddOp(v, OP_MakeRecord, 1, 0); - seekOp = OP_MoveGt; - } - sqlite3VdbeAddOp(v, seekOp, base+1, 0); - sqlite3VdbeAddOp(v, OP_IdxRecno, base+1, 0); - sqlite3VdbeAddOp(v, OP_Close, base+1, 0); - sqlite3VdbeAddOp(v, OP_MoveGe, base, 0); - } - eList.nExpr = 1; - memset(&eListItem, 0, sizeof(eListItem)); - eList.a = &eListItem; - eList.a[0].pExpr = pExpr; - selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, cont, cont, 0); - sqlite3VdbeResolveLabel(v, cont); - sqlite3VdbeAddOp(v, OP_Close, base, 0); - - return 1; -} - -/* -** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return -** the number of errors seen. -** -** An ORDER BY or GROUP BY is a list of expressions. If any expression -** is an integer constant, then that expression is replaced by the -** corresponding entry in the result set. -*/ -static int processOrderGroupBy( - Parse *pParse, /* Parsing context */ - ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */ - SrcList *pTabList, /* The FROM clause */ - ExprList *pEList, /* The result set */ - int isAgg, /* True if aggregate functions are involved */ - const char *zType /* Either "ORDER" or "GROUP", as appropriate */ -){ - int i; - if( pOrderBy==0 ) return 0; - for(i=0; i<pOrderBy->nExpr; i++){ - int iCol; - Expr *pE = pOrderBy->a[i].pExpr; - if( sqlite3ExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){ - sqlite3ExprDelete(pE); - pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr); - } - if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList, pE, isAgg, 0) ){ - return 1; - } - if( sqlite3ExprIsConstant(pE) ){ - if( sqlite3ExprIsInteger(pE, &iCol)==0 ){ - sqlite3ErrorMsg(pParse, - "%s BY terms must not be non-integer constants", zType); - return 1; - }else if( iCol<=0 || iCol>pEList->nExpr ){ - sqlite3ErrorMsg(pParse, - "%s BY column number %d out of range - should be " - "between 1 and %d", zType, iCol, pEList->nExpr); - return 1; - } - } - } - return 0; -} - -/* -** Generate code for the given SELECT statement. -** -** The results are distributed in various ways depending on the -** value of eDest and iParm. -** -** eDest Value Result -** ------------ ------------------------------------------- -** SRT_Callback Invoke the callback for each row of the result. -** -** SRT_Mem Store first result in memory cell iParm -** -** SRT_Set Store results as keys of table iParm. -** -** SRT_Union Store results as a key in a temporary table iParm -** -** SRT_Except Remove results from the temporary table iParm. -** -** SRT_Table Store results in temporary table iParm -** -** The table above is incomplete. Additional eDist value have be added -** since this comment was written. See the selectInnerLoop() function for -** a complete listing of the allowed values of eDest and their meanings. -** -** This routine returns the number of errors. If any errors are -** encountered, then an appropriate error message is left in -** pParse->zErrMsg. -** -** This routine does NOT free the Select structure passed in. The -** calling function needs to do that. -** -** The pParent, parentTab, and *pParentAgg fields are filled in if this -** SELECT is a subquery. This routine may try to combine this SELECT -** with its parent to form a single flat query. In so doing, it might -** change the parent query from a non-aggregate to an aggregate query. -** For that reason, the pParentAgg flag is passed as a pointer, so it -** can be changed. -** -** Example 1: The meaning of the pParent parameter. -** -** SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3; -** \ \_______ subquery _______/ / -** \ / -** \____________________ outer query ___________________/ -** -** This routine is called for the outer query first. For that call, -** pParent will be NULL. During the processing of the outer query, this -** routine is called recursively to handle the subquery. For the recursive -** call, pParent will point to the outer query. Because the subquery is -** the second element in a three-way join, the parentTab parameter will -** be 1 (the 2nd value of a 0-indexed array.) -*/ -int sqlite3Select( - Parse *pParse, /* The parser context */ - Select *p, /* The SELECT statement being coded. */ - int eDest, /* How to dispose of the results */ - int iParm, /* A parameter used by the eDest disposal method */ - Select *pParent, /* Another SELECT for which this is a sub-query */ - int parentTab, /* Index in pParent->pSrc of this query */ - int *pParentAgg, /* True if pParent uses aggregate functions */ - char *aff /* If eDest is SRT_Union, the affinity string */ -){ - int i; - WhereInfo *pWInfo; - Vdbe *v; - int isAgg = 0; /* True for select lists like "count(*)" */ - ExprList *pEList; /* List of columns to extract. */ - SrcList *pTabList; /* List of tables to select from */ - Expr *pWhere; /* The WHERE clause. May be NULL */ - ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */ - ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */ - Expr *pHaving; /* The HAVING clause. May be NULL */ - int isDistinct; /* True if the DISTINCT keyword is present */ - int distinct; /* Table to use for the distinct set */ - int rc = 1; /* Value to return from this function */ - - if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1; - if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; - - /* If there is are a sequence of queries, do the earlier ones first. - */ - if( p->pPrior ){ - return multiSelect(pParse, p, eDest, iParm, aff); - } - - /* Make local copies of the parameters for this query. - */ - pTabList = p->pSrc; - pWhere = p->pWhere; - pOrderBy = p->pOrderBy; - pGroupBy = p->pGroupBy; - pHaving = p->pHaving; - isDistinct = p->isDistinct; - - /* Allocate VDBE cursors for each table in the FROM clause - */ - sqlite3SrcListAssignCursors(pParse, pTabList); - - /* - ** Do not even attempt to generate any code if we have already seen - ** errors before this routine starts. - */ - if( pParse->nErr>0 ) goto select_end; - - /* Expand any "*" terms in the result set. (For example the "*" in - ** "SELECT * FROM t1") The fillInColumnlist() routine also does some - ** other housekeeping - see the header comment for details. - */ - if( fillInColumnList(pParse, p) ){ - goto select_end; - } - pWhere = p->pWhere; - pEList = p->pEList; - if( pEList==0 ) goto select_end; - - /* If writing to memory or generating a set - ** only a single column may be output. - */ - if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){ - sqlite3ErrorMsg(pParse, "only a single result allowed for " - "a SELECT that is part of an expression"); - goto select_end; - } - - /* ORDER BY is ignored for some destinations. - */ - switch( eDest ){ - case SRT_Union: - case SRT_Except: - case SRT_Discard: - pOrderBy = 0; - break; - default: - break; - } - - /* At this point, we should have allocated all the cursors that we - ** need to handle subquerys and temporary tables. - ** - ** Resolve the column names and do a semantics check on all the expressions. - */ - for(i=0; i<pEList->nExpr; i++){ - if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pEList->a[i].pExpr, - 1, &isAgg) ){ - goto select_end; - } - } - if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList, pWhere, 0, 0) ){ - goto select_end; - } - if( pHaving ){ - if( pGroupBy==0 ){ - sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); - goto select_end; - } - if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList,pHaving,1,&isAgg) ){ - goto select_end; - } - } - if( processOrderGroupBy(pParse, pOrderBy, pTabList, pEList, isAgg, "ORDER") - || processOrderGroupBy(pParse, pGroupBy, pTabList, pEList, isAgg, "GROUP") - ){ - goto select_end; - } - - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto select_end; - - /* Identify column names if we will be using them in a callback. This - ** step is skipped if the output is going to some other destination. - */ - if( eDest==SRT_Callback ){ - generateColumnNames(pParse, pTabList, pEList); - } - - /* Generate code for all sub-queries in the FROM clause - */ - for(i=0; i<pTabList->nSrc; i++){ - const char *zSavedAuthContext = 0; - int needRestoreContext; - - if( pTabList->a[i].pSelect==0 ) continue; - if( pTabList->a[i].zName!=0 ){ - zSavedAuthContext = pParse->zAuthContext; - pParse->zAuthContext = pTabList->a[i].zName; - needRestoreContext = 1; - }else{ - needRestoreContext = 0; - } - sqlite3Select(pParse, pTabList->a[i].pSelect, SRT_TempTable, - pTabList->a[i].iCursor, p, i, &isAgg, 0); - if( needRestoreContext ){ - pParse->zAuthContext = zSavedAuthContext; - } - pTabList = p->pSrc; - pWhere = p->pWhere; - if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){ - pOrderBy = p->pOrderBy; - } - pGroupBy = p->pGroupBy; - pHaving = p->pHaving; - isDistinct = p->isDistinct; - } - - /* Check for the special case of a min() or max() function by itself - ** in the result set. - */ - if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){ - rc = 0; - goto select_end; - } - - /* Check to see if this is a subquery that can be "flattened" into its parent. - ** If flattening is a possiblity, do so and return immediately. - */ - if( pParent && pParentAgg && - flattenSubquery(pParse, pParent, parentTab, *pParentAgg, isAgg) ){ - if( isAgg ) *pParentAgg = 1; - return rc; - } - - /* If there is an ORDER BY clause, resolve any collation sequences - ** names that have been explicitly specified. - */ - if( pOrderBy ){ - for(i=0; i<pOrderBy->nExpr; i++){ - if( pOrderBy->a[i].zName ){ - pOrderBy->a[i].pExpr->pColl = - sqlite3LocateCollSeq(pParse, pOrderBy->a[i].zName, -1); - } - } - if( pParse->nErr ){ - goto select_end; - } - } - - /* Set the limiter. - */ - computeLimitRegisters(pParse, p); - - /* If the output is destined for a temporary table, open that table. - */ - if( eDest==SRT_TempTable ){ - sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, pEList->nExpr); - } - - /* Do an analysis of aggregate expressions. - */ - sqliteAggregateInfoReset(pParse); - if( isAgg || pGroupBy ){ - assert( pParse->nAgg==0 ); - isAgg = 1; - for(i=0; i<pEList->nExpr; i++){ - if( sqlite3ExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){ - goto select_end; - } - } - if( pGroupBy ){ - for(i=0; i<pGroupBy->nExpr; i++){ - if( sqlite3ExprAnalyzeAggregates(pParse, pGroupBy->a[i].pExpr) ){ - goto select_end; - } - } - } - if( pHaving && sqlite3ExprAnalyzeAggregates(pParse, pHaving) ){ - goto select_end; - } - if( pOrderBy ){ - for(i=0; i<pOrderBy->nExpr; i++){ - if( sqlite3ExprAnalyzeAggregates(pParse, pOrderBy->a[i].pExpr) ){ - goto select_end; - } - } - } - } - - /* Reset the aggregator - */ - if( isAgg ){ - int addr = sqlite3VdbeAddOp(v, OP_AggReset, (pGroupBy?0:1), pParse->nAgg); - for(i=0; i<pParse->nAgg; i++){ - FuncDef *pFunc; - if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){ - sqlite3VdbeOp3(v, OP_AggInit, 0, i, (char*)pFunc, P3_FUNCDEF); - } - } - if( pGroupBy ){ - int sz = sizeof(KeyInfo) + pGroupBy->nExpr*sizeof(CollSeq*); - KeyInfo *pKey = (KeyInfo *)sqliteMalloc(sz); - if( 0==pKey ){ - goto select_end; - } - pKey->enc = pParse->db->enc; - pKey->nField = pGroupBy->nExpr; - for(i=0; i<pGroupBy->nExpr; i++){ - pKey->aColl[i] = sqlite3ExprCollSeq(pParse, pGroupBy->a[i].pExpr); - if( !pKey->aColl[i] ){ - pKey->aColl[i] = pParse->db->pDfltColl; - } - } - sqlite3VdbeChangeP3(v, addr, (char *)pKey, P3_KEYINFO_HANDOFF); - } - } - - /* Initialize the memory cell to NULL - */ - if( eDest==SRT_Mem ){ - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1); - } - - /* Open a temporary table to use for the distinct set. - */ - if( isDistinct ){ - distinct = pParse->nTab++; - openTempIndex(pParse, p, distinct, 0); - }else{ - distinct = -1; - } - - /* Begin the database scan - */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, - pGroupBy ? 0 : &pOrderBy); - if( pWInfo==0 ) goto select_end; - - /* Use the standard inner loop if we are not dealing with - ** aggregates - */ - if( !isAgg ){ - if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, - iParm, pWInfo->iContinue, pWInfo->iBreak, aff) ){ - goto select_end; - } - } - - /* If we are dealing with aggregates, then do the special aggregate - ** processing. - */ - else{ - AggExpr *pAgg; - if( pGroupBy ){ - int lbl1; - for(i=0; i<pGroupBy->nExpr; i++){ - sqlite3ExprCode(pParse, pGroupBy->a[i].pExpr); - } - /* No affinity string is attached to the following OP_MakeRecord - ** because we do not need to do any coercion of datatypes. */ - sqlite3VdbeAddOp(v, OP_MakeRecord, pGroupBy->nExpr, 0); - lbl1 = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_AggFocus, 0, lbl1); - for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){ - if( pAgg->isAgg ) continue; - sqlite3ExprCode(pParse, pAgg->pExpr); - sqlite3VdbeAddOp(v, OP_AggSet, 0, i); - } - sqlite3VdbeResolveLabel(v, lbl1); - } - for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){ - Expr *pE; - int nExpr; - FuncDef *pDef; - if( !pAgg->isAgg ) continue; - assert( pAgg->pFunc!=0 ); - assert( pAgg->pFunc->xStep!=0 ); - pDef = pAgg->pFunc; - pE = pAgg->pExpr; - assert( pE!=0 ); - assert( pE->op==TK_AGG_FUNCTION ); - nExpr = sqlite3ExprCodeExprList(pParse, pE->pList); - sqlite3VdbeAddOp(v, OP_Integer, i, 0); - if( pDef->needCollSeq ){ - CollSeq *pColl = 0; - int j; - for(j=0; !pColl && j<nExpr; j++){ - pColl = sqlite3ExprCollSeq(pParse, pE->pList->a[j].pExpr); - } - if( !pColl ) pColl = pParse->db->pDfltColl; - sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ); - } - sqlite3VdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_POINTER); - } - } - - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - - /* If we are processing aggregates, we need to set up a second loop - ** over all of the aggregate values and process them. - */ - if( isAgg ){ - int endagg = sqlite3VdbeMakeLabel(v); - int startagg; - startagg = sqlite3VdbeAddOp(v, OP_AggNext, 0, endagg); - pParse->useAgg = 1; - if( pHaving ){ - sqlite3ExprIfFalse(pParse, pHaving, startagg, 1); - } - if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, - iParm, startagg, endagg, aff) ){ - goto select_end; - } - sqlite3VdbeAddOp(v, OP_Goto, 0, startagg); - sqlite3VdbeResolveLabel(v, endagg); - sqlite3VdbeAddOp(v, OP_Noop, 0, 0); - pParse->useAgg = 0; - } - - /* If there is an ORDER BY clause, then we need to sort the results - ** and send them to the callback one by one. - */ - if( pOrderBy ){ - generateSortTail(pParse, p, v, pEList->nExpr, eDest, iParm); - } - - /* If this was a subquery, we have now converted the subquery into a - ** temporary table. So delete the subquery structure from the parent - ** to prevent this subquery from being evaluated again and to force the - ** the use of the temporary table. - */ - if( pParent ){ - assert( pParent->pSrc->nSrc>parentTab ); - assert( pParent->pSrc->a[parentTab].pSelect==p ); - sqlite3SelectDelete(p); - pParent->pSrc->a[parentTab].pSelect = 0; - } - - /* The SELECT was successfully coded. Set the return code to 0 - ** to indicate no errors. - */ - rc = 0; - - /* Control jumps to here if an error is encountered above, or upon - ** successful coding of the SELECT. - */ -select_end: - sqliteAggregateInfoReset(pParse); - return rc; -} diff --git a/kopete/plugins/statistics/sqlite/shell.c b/kopete/plugins/statistics/sqlite/shell.c deleted file mode 100644 index bdd13cc9..00000000 --- a/kopete/plugins/statistics/sqlite/shell.c +++ /dev/null @@ -1,1786 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement the "sqlite" command line -** utility for accessing SQLite databases. -** -** $Id$ -*/ -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <assert.h> -#include "sqlite3.h" -#include <ctype.h> - -#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) -# include <signal.h> -# include <pwd.h> -# include <unistd.h> -# include <sys/types.h> -#endif - -#ifdef __MACOS__ -# include <console.h> -# include <signal.h> -# include <unistd.h> -# include <extras.h> -# include <Files.h> -# include <Folders.h> -#endif - -#if defined(HAVE_READLINE) && HAVE_READLINE==1 -# include <readline/readline.h> -# include <readline/history.h> -#else -# define readline(p) local_getline(p,stdin) -# define add_history(X) -# define read_history(X) -# define write_history(X) -# define stifle_history(X) -#endif - -/* Make sure isatty() has a prototype. -*/ -extern int isatty(); - -/* -** The following is the open SQLite database. We make a pointer -** to this database a static variable so that it can be accessed -** by the SIGINT handler to interrupt database processing. -*/ -static sqlite3 *db = 0; - -/* -** True if an interrupt (Control-C) has been received. -*/ -static int seenInterrupt = 0; - -/* -** This is the name of our program. It is set in main(), used -** in a number of other places, mostly for error messages. -*/ -static char *Argv0; - -/* -** Prompt strings. Initialized in main. Settable with -** .prompt main continue -*/ -static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ -static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ - - -/* -** Determines if a string is a number of not. -*/ -static int isNumber(const unsigned char *z, int *realnum){ - if( *z=='-' || *z=='+' ) z++; - if( !isdigit(*z) ){ - return 0; - } - z++; - if( realnum ) *realnum = 0; - while( isdigit(*z) ){ z++; } - if( *z=='.' ){ - z++; - if( !isdigit(*z) ) return 0; - while( isdigit(*z) ){ z++; } - if( realnum ) *realnum = 1; - } - if( *z=='e' || *z=='E' ){ - z++; - if( *z=='+' || *z=='-' ) z++; - if( !isdigit(*z) ) return 0; - while( isdigit(*z) ){ z++; } - if( realnum ) *realnum = 1; - } - return *z==0; -} - -/* -** A global char* and an SQL function to access its current value -** from within an SQL statement. This program used to use the -** sqlite_exec_printf() API to substitue a string into an SQL statement. -** The correct way to do this with sqlite3 is to use the bind API, but -** since the shell is built around the callback paradigm it would be a lot -** of work. Instead just use this hack, which is quite harmless. -*/ -static const char *zShellStatic = 0; -static void shellstaticFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - assert( 0==argc ); - assert( zShellStatic ); - sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC); -} - - -/* -** This routine reads a line of text from FILE in, stores -** the text in memory obtained from malloc() and returns a pointer -** to the text. NULL is returned at end of file, or if malloc() -** fails. -** -** The interface is like "readline" but no command-line editing -** is done. -*/ -static char *local_getline(char *zPrompt, FILE *in){ - char *zLine; - int nLine; - int n; - int eol; - - if( zPrompt && *zPrompt ){ - printf("%s",zPrompt); - fflush(stdout); - } - nLine = 100; - zLine = malloc( nLine ); - if( zLine==0 ) return 0; - n = 0; - eol = 0; - while( !eol ){ - if( n+100>nLine ){ - nLine = nLine*2 + 100; - zLine = realloc(zLine, nLine); - if( zLine==0 ) return 0; - } - if( fgets(&zLine[n], nLine - n, in)==0 ){ - if( n==0 ){ - free(zLine); - return 0; - } - zLine[n] = 0; - eol = 1; - break; - } - while( zLine[n] ){ n++; } - if( n>0 && zLine[n-1]=='\n' ){ - n--; - zLine[n] = 0; - eol = 1; - } - } - zLine = realloc( zLine, n+1 ); - return zLine; -} - -/* -** Retrieve a single line of input text. "isatty" is true if text -** is coming from a terminal. In that case, we issue a prompt and -** attempt to use "readline" for command-line editing. If "isatty" -** is false, use "local_getline" instead of "readline" and issue no prompt. -** -** zPrior is a string of prior text retrieved. If not the empty -** string, then issue a continuation prompt. -*/ -static char *one_input_line(const char *zPrior, FILE *in){ - char *zPrompt; - char *zResult; - if( in!=0 ){ - return local_getline(0, in); - } - if( zPrior && zPrior[0] ){ - zPrompt = continuePrompt; - }else{ - zPrompt = mainPrompt; - } - zResult = readline(zPrompt); - if( zResult ) add_history(zResult); - return zResult; -} - -struct previous_mode_data { - int valid; /* Is there legit data in here? */ - int mode; - int showHeader; - int colWidth[100]; -}; -/* -** An pointer to an instance of this structure is passed from -** the main program to the callback. This is used to communicate -** state and mode information. -*/ -struct callback_data { - sqlite3 *db; /* The database */ - int echoOn; /* True to echo input commands */ - int cnt; /* Number of records displayed so far */ - FILE *out; /* Write results here */ - int mode; /* An output mode setting */ - int showHeader; /* True to show column names in List or Column mode */ - char *zDestTable; /* Name of destination table when MODE_Insert */ - char separator[20]; /* Separator character for MODE_List */ - int colWidth[100]; /* Requested width of each column when in column mode*/ - int actualWidth[100]; /* Actual width of each column */ - char nullvalue[20]; /* The text to print when a NULL comes back from - ** the database */ - struct previous_mode_data explainPrev; - /* Holds the mode information just before - ** .explain ON */ - char outfile[FILENAME_MAX]; /* Filename for *out */ - const char *zDbFilename; /* name of the database file */ - char *zKey; /* Encryption key */ -}; - -/* -** These are the allowed modes. -*/ -#define MODE_Line 0 /* One column per line. Blank line between records */ -#define MODE_Column 1 /* One record per line in neat columns */ -#define MODE_List 2 /* One record per line with a separator */ -#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ -#define MODE_Html 4 /* Generate an XHTML table */ -#define MODE_Insert 5 /* Generate SQL "insert" statements */ -#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ -#define MODE_Csv 7 /* Quote strings, numbers are plain */ -#define MODE_NUM_OF 8 /* The number of modes (not a mode itself) */ - -char *modeDescr[MODE_NUM_OF] = { - "line", - "column", - "list", - "semi", - "html", - "insert", - "tcl", - "csv", -}; - -/* -** Number of elements in an array -*/ -#define ArraySize(X) (sizeof(X)/sizeof(X[0])) - -/* -** Output the given string as a quoted string using SQL quoting conventions. -*/ -static void output_quoted_string(FILE *out, const char *z){ - int i; - int nSingle = 0; - for(i=0; z[i]; i++){ - if( z[i]=='\'' ) nSingle++; - } - if( nSingle==0 ){ - fprintf(out,"'%s'",z); - }else{ - fprintf(out,"'"); - while( *z ){ - for(i=0; z[i] && z[i]!='\''; i++){} - if( i==0 ){ - fprintf(out,"''"); - z++; - }else if( z[i]=='\'' ){ - fprintf(out,"%.*s''",i,z); - z += i+1; - }else{ - fprintf(out,"%s",z); - break; - } - } - fprintf(out,"'"); - } -} - -/* -** Output the given string as a quoted according to C or TCL quoting rules. -*/ -static void output_c_string(FILE *out, const char *z){ - unsigned int c; - fputc('"', out); - while( (c = *(z++))!=0 ){ - if( c=='\\' ){ - fputc(c, out); - fputc(c, out); - }else if( c=='\t' ){ - fputc('\\', out); - fputc('t', out); - }else if( c=='\n' ){ - fputc('\\', out); - fputc('n', out); - }else if( c=='\r' ){ - fputc('\\', out); - fputc('r', out); - }else if( !isprint(c) ){ - fprintf(out, "\\%03o", c); - }else{ - fputc(c, out); - } - } - fputc('"', out); -} - -/* -** Output the given string with characters that are special to -** HTML escaped. -*/ -static void output_html_string(FILE *out, const char *z){ - int i; - while( *z ){ - for(i=0; z[i] && z[i]!='<' && z[i]!='&'; i++){} - if( i>0 ){ - fprintf(out,"%.*s",i,z); - } - if( z[i]=='<' ){ - fprintf(out,"<"); - }else if( z[i]=='&' ){ - fprintf(out,"&"); - }else{ - break; - } - z += i + 1; - } -} - -/* -** Output a single term of CSV. Actually, p->separator is used for -** the separator, which may or may not be a comma. p->nullvalue is -** the null value. Strings are quoted using ANSI-C rules. Numbers -** appear outside of quotes. -*/ -static void output_csv(struct callback_data *p, const char *z, int bSep){ - if( z==0 ){ - fprintf(p->out,"%s",p->nullvalue); - }else if( isNumber(z, 0) ){ - fprintf(p->out,"%s",z); - }else{ - output_c_string(p->out, z); - } - if( bSep ){ - fprintf(p->out, p->separator); - } -} - -/* -** This routine runs when the user presses Ctrl-C -*/ -static void interrupt_handler(int NotUsed){ - seenInterrupt = 1; - if( db ) sqlite3_interrupt(db); -} - -/* -** This is the callback routine that the SQLite library -** invokes for each row of a query result. -*/ -static int callback(void *pArg, int nArg, char **azArg, char **azCol){ - int i; - struct callback_data *p = (struct callback_data*)pArg; - switch( p->mode ){ - case MODE_Line: { - int w = 5; - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - int len = strlen(azCol[i]); - if( len>w ) w = len; - } - if( p->cnt++>0 ) fprintf(p->out,"\n"); - for(i=0; i<nArg; i++){ - fprintf(p->out,"%*s = %s\n", w, azCol[i], - azArg[i] ? azArg[i] : p->nullvalue); - } - break; - } - case MODE_Column: { - if( p->cnt++==0 ){ - for(i=0; i<nArg; i++){ - int w, n; - if( i<ArraySize(p->colWidth) ){ - w = p->colWidth[i]; - }else{ - w = 0; - } - if( w<=0 ){ - w = strlen(azCol[i] ? azCol[i] : ""); - if( w<10 ) w = 10; - n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue); - if( w<n ) w = n; - } - if( i<ArraySize(p->actualWidth) ){ - p->actualWidth[i] = w; - } - if( p->showHeader ){ - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); - } - } - if( p->showHeader ){ - for(i=0; i<nArg; i++){ - int w; - if( i<ArraySize(p->actualWidth) ){ - w = p->actualWidth[i]; - }else{ - w = 10; - } - fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" - "----------------------------------------------------------", - i==nArg-1 ? "\n": " "); - } - } - } - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - int w; - if( i<ArraySize(p->actualWidth) ){ - w = p->actualWidth[i]; - }else{ - w = 10; - } - fprintf(p->out,"%-*.*s%s",w,w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); - } - break; - } - case MODE_Semi: - case MODE_List: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; i<nArg; i++){ - fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); - } - } - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - char *z = azArg[i]; - if( z==0 ) z = p->nullvalue; - fprintf(p->out, "%s", z); - if( i<nArg-1 ){ - fprintf(p->out, "%s", p->separator); - }else if( p->mode==MODE_Semi ){ - fprintf(p->out, ";\n"); - }else{ - fprintf(p->out, "\n"); - } - } - break; - } - case MODE_Html: { - if( p->cnt++==0 && p->showHeader ){ - fprintf(p->out,"<TR>"); - for(i=0; i<nArg; i++){ - fprintf(p->out,"<TH>%s</TH>",azCol[i]); - } - fprintf(p->out,"</TR>\n"); - } - if( azArg==0 ) break; - fprintf(p->out,"<TR>"); - for(i=0; i<nArg; i++){ - fprintf(p->out,"<TD>"); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); - fprintf(p->out,"</TD>\n"); - } - fprintf(p->out,"</TR>\n"); - break; - } - case MODE_Tcl: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; i<nArg; i++){ - output_c_string(p->out,azCol[i]); - fprintf(p->out, "%s", p->separator); - } - fprintf(p->out,"\n"); - } - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); - fprintf(p->out, "%s", p->separator); - } - fprintf(p->out,"\n"); - break; - } - case MODE_Csv: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; i<nArg; i++){ - output_csv(p, azCol[i], i<nArg-1); - } - fprintf(p->out,"\n"); - } - if( azArg==0 ) break; - for(i=0; i<nArg; i++){ - output_csv(p, azArg[i], i<nArg-1); - } - fprintf(p->out,"\n"); - break; - } - case MODE_Insert: { - if( azArg==0 ) break; - fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable); - for(i=0; i<nArg; i++){ - char *zSep = i>0 ? ",": ""; - if( azArg[i]==0 ){ - fprintf(p->out,"%sNULL",zSep); - }else if( isNumber(azArg[i], 0) ){ - fprintf(p->out,"%s%s",zSep, azArg[i]); - }else{ - if( zSep[0] ) fprintf(p->out,"%s",zSep); - output_quoted_string(p->out, azArg[i]); - } - } - fprintf(p->out,");\n"); - break; - } - } - return 0; -} - -/* -** Set the destination table field of the callback_data structure to -** the name of the table given. Escape any quote characters in the -** table name. -*/ -static void set_table_name(struct callback_data *p, const char *zName){ - int i, n; - int needQuote; - char *z; - - if( p->zDestTable ){ - free(p->zDestTable); - p->zDestTable = 0; - } - if( zName==0 ) return; - needQuote = !isalpha((unsigned char)*zName) && *zName!='_'; - for(i=n=0; zName[i]; i++, n++){ - if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){ - needQuote = 1; - if( zName[i]=='\'' ) n++; - } - } - if( needQuote ) n += 2; - z = p->zDestTable = malloc( n+1 ); - if( z==0 ){ - fprintf(stderr,"Out of memory!\n"); - exit(1); - } - n = 0; - if( needQuote ) z[n++] = '\''; - for(i=0; zName[i]; i++){ - z[n++] = zName[i]; - if( zName[i]=='\'' ) z[n++] = '\''; - } - if( needQuote ) z[n++] = '\''; - z[n] = 0; -} - -/* zIn is either a pointer to a NULL-terminated string in memory obtained -** from malloc(), or a NULL pointer. The string pointed to by zAppend is -** added to zIn, and the result returned in memory obtained from malloc(). -** zIn, if it was not NULL, is freed. -** -** If the third argument, quote, is not '\0', then it is used as a -** quote character for zAppend. -*/ -static char * appendText(char *zIn, char const *zAppend, char quote){ - int len; - int i; - int nAppend = strlen(zAppend); - int nIn = (zIn?strlen(zIn):0); - - len = nAppend+nIn+1; - if( quote ){ - len += 2; - for(i=0; i<nAppend; i++){ - if( zAppend[i]==quote ) len++; - } - } - - zIn = (char *)realloc(zIn, len); - if( !zIn ){ - return 0; - } - - if( quote ){ - char *zCsr = &zIn[nIn]; - *zCsr++ = quote; - for(i=0; i<nAppend; i++){ - *zCsr++ = zAppend[i]; - if( zAppend[i]==quote ) *zCsr++ = quote; - } - *zCsr++ = quote; - *zCsr++ = '\0'; - assert( (zCsr-zIn)==len ); - }else{ - memcpy(&zIn[nIn], zAppend, nAppend); - zIn[len-1] = '\0'; - } - - return zIn; -} - - -/* -** Execute a query statement that has a single result column. Print -** that result column on a line by itself with a semicolon terminator. -*/ -static int run_table_dump_query(FILE *out, sqlite3 *db, const char *zSelect){ - sqlite3_stmt *pSelect; - int rc; - rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0); - if( rc!=SQLITE_OK || !pSelect ){ - return rc; - } - rc = sqlite3_step(pSelect); - while( rc==SQLITE_ROW ){ - fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0)); - rc = sqlite3_step(pSelect); - } - return sqlite3_finalize(pSelect); -} - - -/* -** This is a different callback routine used for dumping the database. -** Each row received by this callback consists of a table name, -** the table type ("index" or "table") and SQL to create the table. -** This routine should print text sufficient to recreate the table. -*/ -static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ - int rc; - const char *zTable; - const char *zType; - const char *zSql; - struct callback_data *p = (struct callback_data *)pArg; - - if( nArg!=3 ) return 1; - zTable = azArg[0]; - zType = azArg[1]; - zSql = azArg[2]; - - fprintf(p->out, "%s;\n", zSql); - - if( strcmp(zType, "table")==0 ){ - sqlite3_stmt *pTableInfo = 0; - char *zSelect = 0; - char *zTableInfo = 0; - char *zTmp = 0; - - zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); - zTableInfo = appendText(zTableInfo, zTable, '"'); - zTableInfo = appendText(zTableInfo, ");", 0); - - rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0); - if( zTableInfo ) free(zTableInfo); - if( rc!=SQLITE_OK || !pTableInfo ){ - return 1; - } - - zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); - zTmp = appendText(zTmp, zTable, '"'); - if( zTmp ){ - zSelect = appendText(zSelect, zTmp, '\''); - } - zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); - rc = sqlite3_step(pTableInfo); - while( rc==SQLITE_ROW ){ - zSelect = appendText(zSelect, "quote(", 0); - zSelect = appendText(zSelect, sqlite3_column_text(pTableInfo, 1), '"'); - rc = sqlite3_step(pTableInfo); - if( rc==SQLITE_ROW ){ - zSelect = appendText(zSelect, ") || ', ' || ", 0); - }else{ - zSelect = appendText(zSelect, ") ", 0); - } - } - rc = sqlite3_finalize(pTableInfo); - if( rc!=SQLITE_OK ){ - if( zSelect ) free(zSelect); - return 1; - } - zSelect = appendText(zSelect, "|| ')' FROM ", 0); - zSelect = appendText(zSelect, zTable, '"'); - - rc = run_table_dump_query(p->out, p->db, zSelect); - if( rc==SQLITE_CORRUPT ){ - zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); - rc = run_table_dump_query(p->out, p->db, zSelect); - } - if( zSelect ) free(zSelect); - if( rc!=SQLITE_OK ){ - return 1; - } - } - return 0; -} - -/* -** Run zQuery. Update dump_callback() as the callback routine. -** If we get a SQLITE_CORRUPT error, rerun the query after appending -** "ORDER BY rowid DESC" to the end. -*/ -static int run_schema_dump_query( - struct callback_data *p, - const char *zQuery, - char **pzErrMsg -){ - int rc; - rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg); - if( rc==SQLITE_CORRUPT ){ - char *zQ2; - int len = strlen(zQuery); - if( pzErrMsg ) sqlite3_free(*pzErrMsg); - zQ2 = malloc( len+100 ); - if( zQ2==0 ) return rc; - sprintf(zQ2, "%s ORDER BY rowid DESC", zQuery); - rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg); - free(zQ2); - } - return rc; -} - -/* -** Text of a help message -*/ -static char zHelp[] = - ".databases List names and files of attached databases\n" - ".dump ?TABLE? ... Dump the database in an SQL text format\n" - ".echo ON|OFF Turn command echo on or off\n" - ".exit Exit this program\n" - ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n" - ".header(s) ON|OFF Turn display of headers on or off\n" - ".help Show this message\n" - ".import FILE TABLE Import data from FILE into TABLE\n" - ".indices TABLE Show names of all indices on TABLE\n" - ".mode MODE ?TABLE? Set output mode where MODE is on of:\n" - " csv Comma-separated values\n" - " column Left-aligned columns. (See .width)\n" - " html HTML <table> code\n" - " insert SQL insert statements for TABLE\n" - " line One value per line\n" - " list Values delimited by .separator string\n" - " tabs Tab-separated values\n" - " tcl TCL list elements\n" - ".nullvalue STRING Print STRING in place of NULL values\n" - ".output FILENAME Send output to FILENAME\n" - ".output stdout Send output to the screen\n" - ".prompt MAIN CONTINUE Replace the standard prompts\n" - ".quit Exit this program\n" - ".read FILENAME Execute SQL in FILENAME\n" -#ifdef SQLITE_HAS_CODEC - ".rekey OLD NEW NEW Change the encryption key\n" -#endif - ".schema ?TABLE? Show the CREATE statements\n" - ".separator STRING Change separator used by output mode and .import\n" - ".show Show the current values for various settings\n" - ".tables ?PATTERN? List names of tables matching a LIKE pattern\n" - ".timeout MS Try opening locked tables for MS milliseconds\n" - ".width NUM NUM ... Set column widths for \"column\" mode\n" -; - -/* Forward reference */ -static void process_input(struct callback_data *p, FILE *in); - -/* -** Make sure the database is open. If it is not, then open it. If -** the database fails to open, print an error message and exit. -*/ -static void open_db(struct callback_data *p){ - if( p->db==0 ){ - sqlite3_open(p->zDbFilename, &p->db); - db = p->db; -#ifdef SQLITE_HAS_CODEC - sqlite3_key(p->db, p->zKey, p->zKey ? strlen(p->zKey) : 0); -#endif - sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0, - shellstaticFunc, 0, 0); - if( SQLITE_OK!=sqlite3_errcode(db) ){ - fprintf(stderr,"Unable to open database \"%s\": %s\n", - p->zDbFilename, sqlite3_errmsg(db)); - exit(1); - } - } -} - -/* -** Do C-language style dequoting. -** -** \t -> tab -** \n -> newline -** \r -> carriage return -** \NNN -> ascii character NNN in octal -** \\ -> backslash -*/ -static void resolve_backslashes(char *z){ - int i, j, c; - for(i=j=0; (c = z[i])!=0; i++, j++){ - if( c=='\\' ){ - c = z[++i]; - if( c=='n' ){ - c = '\n'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='r' ){ - c = '\r'; - }else if( c>='0' && c<='7' ){ - c =- '0'; - if( z[i+1]>='0' && z[i+1]<='7' ){ - i++; - c = (c<<3) + z[i] - '0'; - if( z[i+1]>='0' && z[i+1]<='7' ){ - i++; - c = (c<<3) + z[i] - '0'; - } - } - } - } - z[j] = c; - } - z[j] = 0; -} - -/* -** If an input line begins with "." then invoke this routine to -** process that line. -** -** Return 1 to exit and 0 to continue. -*/ -static int do_meta_command(char *zLine, struct callback_data *p){ - int i = 1; - int nArg = 0; - int n, c; - int rc = 0; - char *azArg[50]; - - /* Parse the input line into tokens. - */ - while( zLine[i] && nArg<ArraySize(azArg) ){ - while( isspace((unsigned char)zLine[i]) ){ i++; } - if( zLine[i]==0 ) break; - if( zLine[i]=='\'' || zLine[i]=='"' ){ - int delim = zLine[i++]; - azArg[nArg++] = &zLine[i]; - while( zLine[i] && zLine[i]!=delim ){ i++; } - if( zLine[i]==delim ){ - zLine[i++] = 0; - } - if( delim=='"' ) resolve_backslashes(azArg[nArg-1]); - }else{ - azArg[nArg++] = &zLine[i]; - while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; } - if( zLine[i] ) zLine[i++] = 0; - resolve_backslashes(azArg[nArg-1]); - } - } - - /* Process the input line. - */ - if( nArg==0 ) return rc; - n = strlen(azArg[0]); - c = azArg[0][0]; - if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ - struct callback_data data; - char *zErrMsg = 0; - open_db(p); - memcpy(&data, p, sizeof(data)); - data.showHeader = 1; - data.mode = MODE_Column; - data.colWidth[0] = 3; - data.colWidth[1] = 15; - data.colWidth[2] = 58; - sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - } - }else - - if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ - char *zErrMsg = 0; - open_db(p); - fprintf(p->out, "BEGIN TRANSACTION;\n"); - if( nArg==1 ){ - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE sql NOT NULL AND type=='table'", 0 - ); - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE sql NOT NULL AND type!='table' AND type!='meta'", 0 - ); - }else{ - int i; - for(i=1; i<nArg; i++){ - zShellStatic = azArg[i]; - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE tbl_name LIKE shellstatic() AND type=='table'" - " AND sql NOT NULL", 0); - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE tbl_name LIKE shellstatic() AND type!='table'" - " AND type!='meta' AND sql NOT NULL", 0); - zShellStatic = 0; - } - } - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - }else{ - fprintf(p->out, "COMMIT;\n"); - } - }else - - if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ - int j; - char *z = azArg[1]; - int val = atoi(azArg[1]); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } - p->echoOn = val; - }else - - if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ - rc = 1; - }else - - if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ - int j; - static char zOne[] = "1"; - char *z = nArg>=2 ? azArg[1] : zOne; - int val = atoi(z); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } - if(val == 1) { - if(!p->explainPrev.valid) { - p->explainPrev.valid = 1; - p->explainPrev.mode = p->mode; - p->explainPrev.showHeader = p->showHeader; - memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth)); - } - /* We could put this code under the !p->explainValid - ** condition so that it does not execute if we are already in - ** explain mode. However, always executing it allows us an easy - ** was to reset to explain mode in case the user previously - ** did an .explain followed by a .width, .mode or .header - ** command. - */ - p->mode = MODE_Column; - p->showHeader = 1; - memset(p->colWidth,0,ArraySize(p->colWidth)); - p->colWidth[0] = 4; - p->colWidth[1] = 12; - p->colWidth[2] = 10; - p->colWidth[3] = 10; - p->colWidth[4] = 35; - }else if (p->explainPrev.valid) { - p->explainPrev.valid = 0; - p->mode = p->explainPrev.mode; - p->showHeader = p->explainPrev.showHeader; - memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); - } - }else - - if( c=='h' && (strncmp(azArg[0], "header", n)==0 - || - strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ - int j; - char *z = azArg[1]; - int val = atoi(azArg[1]); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } - p->showHeader = val; - }else - - if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ - fprintf(stderr,zHelp); - }else - - if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){ - char *zTable = azArg[2]; /* Insert data into this table */ - char *zFile = azArg[1]; /* The file from which to extract data */ - sqlite3_stmt *pStmt; /* A statement */ - int rc; /* Result code */ - int nCol; /* Number of columns in the table */ - int nByte; /* Number of bytes in an SQL string */ - int i, j; /* Loop counters */ - int nSep; /* Number of bytes in p->separator[] */ - char *zSql; /* An SQL statement */ - char *zLine; /* A single line of input from the file */ - char **azCol; /* zLine[] broken up into columns */ - char *zCommit; /* How to commit changes */ - FILE *in; /* The input file */ - int lineno = 0; /* Line number of input file */ - - nSep = strlen(p->separator); - if( nSep==0 ){ - fprintf(stderr, "non-null separator required for import\n"); - return 0; - } - zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); - if( zSql==0 ) return 0; - nByte = strlen(zSql); - rc = sqlite3_prepare(p->db, zSql, 0, &pStmt, 0); - sqlite3_free(zSql); - if( rc ){ - fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); - nCol = 0; - }else{ - nCol = sqlite3_column_count(pStmt); - } - sqlite3_finalize(pStmt); - if( nCol==0 ) return 0; - zSql = malloc( nByte + 20 + nCol*2 ); - if( zSql==0 ) return 0; - sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable); - j = strlen(zSql); - for(i=1; i<nCol; i++){ - zSql[j++] = ','; - zSql[j++] = '?'; - } - zSql[j++] = ')'; - zSql[j] = 0; - rc = sqlite3_prepare(p->db, zSql, 0, &pStmt, 0); - free(zSql); - if( rc ){ - fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); - sqlite3_finalize(pStmt); - return 0; - } - in = fopen(zFile, "rb"); - if( in==0 ){ - fprintf(stderr, "cannot open file: %s\n", zFile); - sqlite3_finalize(pStmt); - return 0; - } - azCol = malloc( sizeof(azCol[0])*(nCol+1) ); - if( azCol==0 ) return 0; - sqlite3_exec(p->db, "BEGIN", 0, 0, 0); - zCommit = "COMMIT"; - while( (zLine = local_getline(0, in))!=0 ){ - char *z; - i = 0; - lineno++; - azCol[0] = zLine; - for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){ - if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){ - *z = 0; - i++; - if( i<nCol ){ - azCol[i] = &z[nSep]; - z += nSep-1; - } - } - } - if( i+1!=nCol ){ - fprintf(stderr,"%s line %d: expected %d columns of data but found %d\n", - zFile, lineno, nCol, i+1); - zCommit = "ROLLBACK"; - break; - } - for(i=0; i<nCol; i++){ - sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); - } - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - free(zLine); - if( rc!=SQLITE_OK ){ - fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); - zCommit = "ROLLBACK"; - break; - } - } - free(azCol); - fclose(in); - sqlite3_finalize(pStmt); - sqlite3_exec(p->db, zCommit, 0, 0, 0); - }else - - if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){ - struct callback_data data; - char *zErrMsg = 0; - open_db(p); - memcpy(&data, p, sizeof(data)); - data.showHeader = 0; - data.mode = MODE_List; - zShellStatic = azArg[1]; - sqlite3_exec(p->db, - "SELECT name FROM sqlite_master " - "WHERE type='index' AND tbl_name LIKE shellstatic() " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type='index' AND tbl_name LIKE shellstatic() " - "ORDER BY 1", - callback, &data, &zErrMsg - ); - zShellStatic = 0; - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - } - }else - - if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){ - int n2 = strlen(azArg[1]); - if( strncmp(azArg[1],"line",n2)==0 - || - strncmp(azArg[1],"lines",n2)==0 ){ - p->mode = MODE_Line; - }else if( strncmp(azArg[1],"column",n2)==0 - || - strncmp(azArg[1],"columns",n2)==0 ){ - p->mode = MODE_Column; - }else if( strncmp(azArg[1],"list",n2)==0 ){ - p->mode = MODE_List; - }else if( strncmp(azArg[1],"html",n2)==0 ){ - p->mode = MODE_Html; - }else if( strncmp(azArg[1],"tcl",n2)==0 ){ - p->mode = MODE_Tcl; - }else if( strncmp(azArg[1],"csv",n2)==0 ){ - p->mode = MODE_Csv; - strcpy(p->separator, ","); - }else if( strncmp(azArg[1],"tabs",n2)==0 ){ - p->mode = MODE_List; - strcpy(p->separator, "\t"); - }else if( strncmp(azArg[1],"insert",n2)==0 ){ - p->mode = MODE_Insert; - if( nArg>=3 ){ - set_table_name(p, azArg[2]); - }else{ - set_table_name(p, "table"); - } - }else { - fprintf(stderr,"mode should be on of: " - "column csv html insert line list tabs tcl\n"); - } - }else - - if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) { - sprintf(p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); - }else - - if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ - if( p->out!=stdout ){ - fclose(p->out); - } - if( strcmp(azArg[1],"stdout")==0 ){ - p->out = stdout; - strcpy(p->outfile,"stdout"); - }else{ - p->out = fopen(azArg[1], "wb"); - if( p->out==0 ){ - fprintf(stderr,"can't write to \"%s\"\n", azArg[1]); - p->out = stdout; - } else { - strcpy(p->outfile,azArg[1]); - } - } - }else - - if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){ - if( nArg >= 2) { - strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); - } - if( nArg >= 3) { - strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); - } - }else - - if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ - rc = 1; - }else - - if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ - FILE *alt = fopen(azArg[1], "rb"); - if( alt==0 ){ - fprintf(stderr,"can't open \"%s\"\n", azArg[1]); - }else{ - process_input(p, alt); - fclose(alt); - } - }else - -#ifdef SQLITE_HAS_CODEC - if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){ - char *zOld = p->zKey; - if( zOld==0 ) zOld = ""; - if( strcmp(azArg[1],zOld) ){ - fprintf(stderr,"old key is incorrect\n"); - }else if( strcmp(azArg[2], azArg[3]) ){ - fprintf(stderr,"2nd copy of new key does not match the 1st\n"); - }else{ - sqlite3_free(p->zKey); - p->zKey = sqlite3_mprintf("%s", azArg[2]); - sqlite3_rekey(p->db, p->zKey, strlen(p->zKey)); - } - }else -#endif - - if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ - struct callback_data data; - char *zErrMsg = 0; - open_db(p); - memcpy(&data, p, sizeof(data)); - data.showHeader = 0; - data.mode = MODE_Semi; - if( nArg>1 ){ - int i; - for(i=0; azArg[1][i]; i++) azArg[1][i] = tolower(azArg[1][i]); - if( strcmp(azArg[1],"sqlite_master")==0 ){ - char *new_argv[2], *new_colv[2]; - new_argv[0] = "CREATE TABLE sqlite_master (\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")"; - new_argv[1] = 0; - new_colv[0] = "sql"; - new_colv[1] = 0; - callback(&data, 1, new_argv, new_colv); - }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){ - char *new_argv[2], *new_colv[2]; - new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")"; - new_argv[1] = 0; - new_colv[0] = "sql"; - new_colv[1] = 0; - callback(&data, 1, new_argv, new_colv); - }else{ - zShellStatic = azArg[1]; - sqlite3_exec(p->db, - "SELECT sql FROM " - " (SELECT * FROM sqlite_master UNION ALL" - " SELECT * FROM sqlite_temp_master) " - "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL " - "ORDER BY substr(type,2,1), name", - callback, &data, &zErrMsg); - zShellStatic = 0; - } - }else{ - sqlite3_exec(p->db, - "SELECT sql FROM " - " (SELECT * FROM sqlite_master UNION ALL" - " SELECT * FROM sqlite_temp_master) " - "WHERE type!='meta' AND sql NOTNULL " - "ORDER BY substr(type,2,1), name", - callback, &data, &zErrMsg - ); - } - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - } - }else - - if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ - sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]); - }else - - if( c=='s' && strncmp(azArg[0], "show", n)==0){ - int i; - fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off"); - fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); - fprintf(p->out,"%9.9s: ", "nullvalue"); - output_c_string(p->out, p->nullvalue); - fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: %s\n","output", - strlen(p->outfile) ? p->outfile : "stdout"); - fprintf(p->out,"%9.9s: ", "separator"); - output_c_string(p->out, p->separator); - fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: ","width"); - for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { - fprintf(p->out,"%d ",p->colWidth[i]); - } - fprintf(p->out,"\n"); - }else - - if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){ - char **azResult; - int nRow, rc; - char *zErrMsg; - open_db(p); - if( nArg==1 ){ - rc = sqlite3_get_table(p->db, - "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type IN ('table','view') " - "ORDER BY 1", - &azResult, &nRow, 0, &zErrMsg - ); - }else{ - zShellStatic = azArg[1]; - rc = sqlite3_get_table(p->db, - "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' " - "ORDER BY 1", - &azResult, &nRow, 0, &zErrMsg - ); - zShellStatic = 0; - } - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - } - if( rc==SQLITE_OK ){ - int len, maxlen = 0; - int i, j; - int nPrintCol, nPrintRow; - for(i=1; i<=nRow; i++){ - if( azResult[i]==0 ) continue; - len = strlen(azResult[i]); - if( len>maxlen ) maxlen = len; - } - nPrintCol = 80/(maxlen+2); - if( nPrintCol<1 ) nPrintCol = 1; - nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; - for(i=0; i<nPrintRow; i++){ - for(j=i+1; j<=nRow; j+=nPrintRow){ - char *zSp = j<=nPrintRow ? "" : " "; - printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); - } - printf("\n"); - } - } - sqlite3_free_table(azResult); - }else - - if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){ - open_db(p); - sqlite3_busy_timeout(p->db, atoi(azArg[1])); - }else - - if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ - int j; - for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ - p->colWidth[j-1] = atoi(azArg[j]); - } - }else - - { - fprintf(stderr, "unknown command or invalid arguments: " - " \"%s\". Enter \".help\" for help\n", azArg[0]); - } - - return rc; -} - -/* -** Return TRUE if the last non-whitespace character in z[] is a semicolon. -** z[] is N characters long. -*/ -static int _ends_with_semicolon(const char *z, int N){ - while( N>0 && isspace((unsigned char)z[N-1]) ){ N--; } - return N>0 && z[N-1]==';'; -} - -/* -** Test to see if a line consists entirely of whitespace. -*/ -static int _all_whitespace(const char *z){ - for(; *z; z++){ - if( isspace(*(unsigned char*)z) ) continue; - if( *z=='/' && z[1]=='*' ){ - z += 2; - while( *z && (*z!='*' || z[1]!='/') ){ z++; } - if( *z==0 ) return 0; - z++; - continue; - } - if( *z=='-' && z[1]=='-' ){ - z += 2; - while( *z && *z!='\n' ){ z++; } - if( *z==0 ) return 1; - continue; - } - return 0; - } - return 1; -} - -/* -** Return TRUE if the line typed in is an SQL command terminator other -** than a semi-colon. The SQL Server style "go" command is understood -** as is the Oracle "/". -*/ -static int _is_command_terminator(const char *zLine){ - while( isspace(*(unsigned char*)zLine) ){ zLine++; }; - if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */ - if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o' - && _all_whitespace(&zLine[2]) ){ - return 1; /* SQL Server */ - } - return 0; -} - -/* -** Read input from *in and process it. If *in==0 then input -** is interactive - the user is typing it it. Otherwise, input -** is coming from a file or device. A prompt is issued and history -** is saved only if input is interactive. An interrupt signal will -** cause this routine to exit immediately, unless input is interactive. -*/ -static void process_input(struct callback_data *p, FILE *in){ - char *zLine; - char *zSql = 0; - int nSql = 0; - char *zErrMsg; - int rc; - while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){ - if( seenInterrupt ){ - if( in!=0 ) break; - seenInterrupt = 0; - } - if( p->echoOn ) printf("%s\n", zLine); - if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; - if( zLine && zLine[0]=='.' && nSql==0 ){ - int rc = do_meta_command(zLine, p); - free(zLine); - if( rc ) break; - continue; - } - if( _is_command_terminator(zLine) ){ - strcpy(zLine,";"); - } - if( zSql==0 ){ - int i; - for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){} - if( zLine[i]!=0 ){ - nSql = strlen(zLine); - zSql = malloc( nSql+1 ); - strcpy(zSql, zLine); - } - }else{ - int len = strlen(zLine); - zSql = realloc( zSql, nSql + len + 2 ); - if( zSql==0 ){ - fprintf(stderr,"%s: out of memory!\n", Argv0); - exit(1); - } - strcpy(&zSql[nSql++], "\n"); - strcpy(&zSql[nSql], zLine); - nSql += len; - } - free(zLine); - if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){ - p->cnt = 0; - open_db(p); - rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg); - if( rc || zErrMsg ){ - if( in!=0 && !p->echoOn ) printf("%s\n",zSql); - if( zErrMsg!=0 ){ - printf("SQL error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - zErrMsg = 0; - }else{ - printf("SQL error: %s\n", sqlite3_errmsg(p->db)); - } - } - free(zSql); - zSql = 0; - nSql = 0; - } - } - if( zSql ){ - if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql); - free(zSql); - } -} - -/* -** Return a pathname which is the user's home directory. A -** 0 return indicates an error of some kind. Space to hold the -** resulting string is obtained from malloc(). The calling -** function should free the result. -*/ -static char *find_home_dir(void){ - char *home_dir = NULL; - -#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) - struct passwd *pwent; - uid_t uid = getuid(); - if( (pwent=getpwuid(uid)) != NULL) { - home_dir = pwent->pw_dir; - } -#endif - -#ifdef __MACOS__ - char home_path[_MAX_PATH+1]; - home_dir = getcwd(home_path, _MAX_PATH); -#endif - - if (!home_dir) { - home_dir = getenv("HOME"); - if (!home_dir) { - home_dir = getenv("HOMEPATH"); /* Windows? */ - } - } - -#if defined(_WIN32) || defined(WIN32) - if (!home_dir) { - home_dir = "c:"; - } -#endif - - if( home_dir ){ - char *z = malloc( strlen(home_dir)+1 ); - if( z ) strcpy(z, home_dir); - home_dir = z; - } - - return home_dir; -} - -/* -** Read input from the file given by sqliterc_override. Or if that -** parameter is NULL, take input from ~/.sqliterc -*/ -static void process_sqliterc( - struct callback_data *p, /* Configuration data */ - const char *sqliterc_override /* Name of config file. NULL to use default */ -){ - char *home_dir = NULL; - const char *sqliterc = sqliterc_override; - char *zBuf; - FILE *in = NULL; - - if (sqliterc == NULL) { - home_dir = find_home_dir(); - if( home_dir==0 ){ - fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0); - return; - } - zBuf = malloc(strlen(home_dir) + 15); - if( zBuf==0 ){ - fprintf(stderr,"%s: out of memory!\n", Argv0); - exit(1); - } - sprintf(zBuf,"%s/.sqliterc",home_dir); - free(home_dir); - sqliterc = (const char*)zBuf; - } - in = fopen(sqliterc,"rb"); - if( in ){ - if( isatty(fileno(stdout)) ){ - printf("Loading resources from %s\n",sqliterc); - } - process_input(p,in); - fclose(in); - } - return; -} - -/* -** Show available command line options -*/ -static const char zOptions[] = - " -init filename read/process named file\n" - " -echo print commands before execution\n" - " -[no]header turn headers on or off\n" - " -column set output mode to 'column'\n" - " -html set output mode to HTML\n" -#ifdef SQLITE_HAS_CODEC - " -key KEY encryption key\n" -#endif - " -line set output mode to 'line'\n" - " -list set output mode to 'list'\n" - " -separator 'x' set output field separator (|)\n" - " -nullvalue 'text' set text string for NULL values\n" - " -version show SQLite version\n" - " -help show this text, also show dot-commands\n" -; -static void usage(int showDetail){ - fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n", Argv0); - if( showDetail ){ - fprintf(stderr, "Options are:\n%s", zOptions); - }else{ - fprintf(stderr, "Use the -help option for additional information\n"); - } - exit(1); -} - -/* -** Initialize the state information in data -*/ -void main_init(struct callback_data *data) { - memset(data, 0, sizeof(*data)); - data->mode = MODE_List; - strcpy(data->separator,"|"); - data->showHeader = 0; - strcpy(mainPrompt,"sqlite> "); - strcpy(continuePrompt," ...> "); -} - -int main(int argc, char **argv){ - char *zErrMsg = 0; - struct callback_data data; - const char *zInitFile = 0; - char *zFirstCmd = 0; - int i; - -#ifdef __MACOS__ - argc = ccommand(&argv); -#endif - - Argv0 = argv[0]; - main_init(&data); - - /* Make sure we have a valid signal handler early, before anything - ** else is done. - */ -#ifdef SIGINT - signal(SIGINT, interrupt_handler); -#endif - - /* Do an initial pass through the command-line argument to locate - ** the name of the database file, the name of the initialization file, - ** and the first command to execute. - */ - for(i=1; i<argc-1; i++){ - if( argv[i][0]!='-' ) break; - if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ - i++; - }else if( strcmp(argv[i],"-init")==0 ){ - i++; - zInitFile = argv[i]; - }else if( strcmp(argv[i],"-key")==0 ){ - i++; - data.zKey = sqlite3_mprintf("%s",argv[i]); - } - } - if( i<argc ){ - data.zDbFilename = argv[i++]; - }else{ - data.zDbFilename = ":memory:"; - } - if( i<argc ){ - zFirstCmd = argv[i++]; - } - data.out = stdout; - - /* Go ahead and open the database file if it already exists. If the - ** file does not exist, delay opening it. This prevents empty database - ** files from being created if a user mistypes the database name argument - ** to the sqlite command-line tool. - */ - if( access(data.zDbFilename, 0)==0 ){ - open_db(&data); - } - - /* Process the initialization file if there is one. If no -init option - ** is given on the command line, look for a file named ~/.sqliterc and - ** try to process it. - */ - process_sqliterc(&data,zInitFile); - - /* Make a second pass through the command-line argument and set - ** options. This second pass is delayed until after the initialization - ** file is processed so that the command-line arguments will override - ** settings in the initialization file. - */ - for(i=1; i<argc && argv[i][0]=='-'; i++){ - char *z = argv[i]; - if( strcmp(z,"-init")==0 || strcmp(z,"-key")==0 ){ - i++; - }else if( strcmp(z,"-html")==0 ){ - data.mode = MODE_Html; - }else if( strcmp(z,"-list")==0 ){ - data.mode = MODE_List; - }else if( strcmp(z,"-line")==0 ){ - data.mode = MODE_Line; - }else if( strcmp(z,"-column")==0 ){ - data.mode = MODE_Column; - }else if( strcmp(z,"-separator")==0 ){ - i++; - sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]); - }else if( strcmp(z,"-nullvalue")==0 ){ - i++; - sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); - }else if( strcmp(z,"-header")==0 ){ - data.showHeader = 1; - }else if( strcmp(z,"-noheader")==0 ){ - data.showHeader = 0; - }else if( strcmp(z,"-echo")==0 ){ - data.echoOn = 1; - }else if( strcmp(z,"-version")==0 ){ - printf("%s\n", sqlite3_libversion()); - return 1; - }else if( strcmp(z,"-help")==0 ){ - usage(1); - }else{ - fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; - } - } - - if( zFirstCmd ){ - /* Run just the command that follows the database name - */ - if( zFirstCmd[0]=='.' ){ - do_meta_command(zFirstCmd, &data); - exit(0); - }else{ - int rc; - open_db(&data); - rc = sqlite3_exec(data.db, zFirstCmd, callback, &data, &zErrMsg); - if( rc!=0 && zErrMsg!=0 ){ - fprintf(stderr,"SQL error: %s\n", zErrMsg); - exit(1); - } - } - }else{ - /* Run commands received from standard input - */ - if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){ - char *zHome; - char *zHistory = 0; - printf( - "SQLite version %s\n" - "Enter \".help\" for instructions\n", - sqlite3_libversion() - ); - zHome = find_home_dir(); - if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){ - sprintf(zHistory,"%s/.sqlite_history", zHome); - } - if( zHistory ) read_history(zHistory); - process_input(&data, 0); - if( zHistory ){ - stifle_history(100); - write_history(zHistory); - } - }else{ - process_input(&data, stdin); - } - } - set_table_name(&data, 0); - if( db ) sqlite3_close(db); - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/sqlite3.h b/kopete/plugins/statistics/sqlite/sqlite3.h deleted file mode 100644 index d6b99049..00000000 --- a/kopete/plugins/statistics/sqlite/sqlite3.h +++ /dev/null @@ -1,1166 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the SQLite library -** presents to client programs. -** -** @(#) $Id$ -*/ -#ifndef _SQLITE3_H_ -#define _SQLITE3_H_ -#include <stdarg.h> /* Needed for the definition of va_list */ - -/* -** Make sure we can call this stuff from C++. -*/ -#ifdef __cplusplus -extern "C" { -#endif - -/* -** The version of the SQLite library. -*/ -#ifdef SQLITE_VERSION -# undef SQLITE_VERSION -#else -# define SQLITE_VERSION "3.0.8" -#endif - -/* -** The version string is also compiled into the library so that a program -** can check to make sure that the lib*.a file and the *.h file are from -** the same version. The sqlite3_libversion() function returns a pointer -** to the sqlite3_version variable - useful in DLLs which cannot access -** global variables. -*/ -extern const char sqlite3_version[]; -const char *sqlite3_libversion(void); - -/* -** Each open sqlite database is represented by an instance of the -** following opaque structure. -*/ -typedef struct sqlite3 sqlite3; - - -/* -** Some compilers do not support the "long long" datatype. So we have -** to do a typedef that for 64-bit integers that depends on what compiler -** is being used. -*/ -#if defined(_MSC_VER) || defined(__BORLANDC__) - typedef __int64 sqlite_int64; - typedef unsigned __int64 sqlite_uint64; -#else - typedef long long int sqlite_int64; - typedef unsigned long long int sqlite_uint64; -#endif - - -/* -** A function to close the database. -** -** Call this function with a pointer to a structure that was previously -** returned from sqlite3_open() and the corresponding database will by closed. -** -** All SQL statements prepared using sqlite3_prepare() or -** sqlite3_prepare16() must be deallocated using sqlite3_finalize() before -** this routine is called. Otherwise, SQLITE_BUSY is returned and the -** database connection remains open. -*/ -int sqlite3_close(sqlite3 *); - -/* -** The type for a callback function. -*/ -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -/* -** A function to executes one or more statements of SQL. -** -** If one or more of the SQL statements are queries, then -** the callback function specified by the 3rd parameter is -** invoked once for each row of the query result. This callback -** should normally return 0. If the callback returns a non-zero -** value then the query is aborted, all subsequent SQL statements -** are skipped and the sqlite3_exec() function returns the SQLITE_ABORT. -** -** The 4th parameter is an arbitrary pointer that is passed -** to the callback function as its first parameter. -** -** The 2nd parameter to the callback function is the number of -** columns in the query result. The 3rd parameter to the callback -** is an array of strings holding the values for each column. -** The 4th parameter to the callback is an array of strings holding -** the names of each column. -** -** The callback function may be NULL, even for queries. A NULL -** callback is not an error. It just means that no callback -** will be invoked. -** -** If an error occurs while parsing or evaluating the SQL (but -** not while executing the callback) then an appropriate error -** message is written into memory obtained from malloc() and -** *errmsg is made to point to that message. The calling function -** is responsible for freeing the memory that holds the error -** message. Use sqlite3_free() for this. If errmsg==NULL, -** then no error message is ever written. -** -** The return value is is SQLITE_OK if there are no errors and -** some other return code if there is an error. The particular -** return value depends on the type of error. -** -** If the query could not be executed because a database file is -** locked or busy, then this function returns SQLITE_BUSY. (This -** behavior can be modified somewhat using the sqlite3_busy_handler() -** and sqlite3_busy_timeout() functions below.) -*/ -int sqlite3_exec( - sqlite3*, /* An open database */ - const char *sql, /* SQL to be executed */ - sqlite3_callback, /* Callback function */ - void *, /* 1st argument to callback function */ - char **errmsg /* Error msg written here */ -); - -/* -** Return values for sqlite3_exec() and sqlite3_step() -*/ -#define SQLITE_OK 0 /* Successful result */ -#define SQLITE_ERROR 1 /* SQL error or missing database */ -#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ -#define SQLITE_PERM 3 /* Access permission denied */ -#define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* The database file is locked */ -#define SQLITE_LOCKED 6 /* A table in the database is locked */ -#define SQLITE_NOMEM 7 /* A malloc() failed */ -#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ -#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ -#define SQLITE_FULL 13 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ -#define SQLITE_EMPTY 16 /* Database is empty */ -#define SQLITE_SCHEMA 17 /* The database schema changed */ -#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ -#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */ -#define SQLITE_MISMATCH 20 /* Data type mismatch */ -#define SQLITE_MISUSE 21 /* Library used incorrectly */ -#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ -#define SQLITE_AUTH 23 /* Authorization denied */ -#define SQLITE_FORMAT 24 /* Auxiliary database format error */ -#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ -#define SQLITE_NOTADB 26 /* File opened that is not a database file */ -#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ -#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ - -/* -** Each entry in an SQLite table has a unique integer key. (The key is -** the value of the INTEGER PRIMARY KEY column if there is such a column, -** otherwise the key is generated at random. The unique key is always -** available as the ROWID, OID, or _ROWID_ column.) The following routine -** returns the integer key of the most recent insert in the database. -** -** This function is similar to the mysql_insert_id() function from MySQL. -*/ -sqlite_int64 sqlite3_last_insert_rowid(sqlite3*); - -/* -** This function returns the number of database rows that were changed -** (or inserted or deleted) by the most recent called sqlite3_exec(). -** -** All changes are counted, even if they were later undone by a -** ROLLBACK or ABORT. Except, changes associated with creating and -** dropping tables are not counted. -** -** If a callback invokes sqlite3_exec() recursively, then the changes -** in the inner, recursive call are counted together with the changes -** in the outer call. -** -** SQLite implements the command "DELETE FROM table" without a WHERE clause -** by dropping and recreating the table. (This is much faster than going -** through and deleting individual elements form the table.) Because of -** this optimization, the change count for "DELETE FROM table" will be -** zero regardless of the number of elements that were originally in the -** table. To get an accurate count of the number of rows deleted, use -** "DELETE FROM table WHERE 1" instead. -*/ -int sqlite3_changes(sqlite3*); - -/* -** This function returns the number of database rows that have been -** modified by INSERT, UPDATE or DELETE statements since the database handle -** was opened. This includes UPDATE, INSERT and DELETE statements executed -** as part of trigger programs. All changes are counted as soon as the -** statement that makes them is completed (when the statement handle is -** passed to sqlite3_reset() or sqlite_finalise()). -** -** SQLite implements the command "DELETE FROM table" without a WHERE clause -** by dropping and recreating the table. (This is much faster than going -** through and deleting individual elements form the table.) Because of -** this optimization, the change count for "DELETE FROM table" will be -** zero regardless of the number of elements that were originally in the -** table. To get an accurate count of the number of rows deleted, use -** "DELETE FROM table WHERE 1" instead. -*/ -int sqlite3_total_changes(sqlite3*); - -/* This function causes any pending database operation to abort and -** return at its earliest opportunity. This routine is typically -** called in response to a user action such as pressing "Cancel" -** or Ctrl-C where the user wants a long query operation to halt -** immediately. -*/ -void sqlite3_interrupt(sqlite3*); - - -/* These functions return true if the given input string comprises -** one or more complete SQL statements. For the sqlite3_complete() call, -** the parameter must be a nul-terminated UTF-8 string. For -** sqlite3_complete16(), a nul-terminated machine byte order UTF-16 string -** is required. -** -** The algorithm is simple. If the last token other than spaces -** and comments is a semicolon, then return true. otherwise return -** false. -*/ -int sqlite3_complete(const char *sql); -int sqlite3_complete16(const void *sql); - -/* -** This routine identifies a callback function that is invoked -** whenever an attempt is made to open a database table that is -** currently locked by another process or thread. If the busy callback -** is NULL, then sqlite3_exec() returns SQLITE_BUSY immediately if -** it finds a locked table. If the busy callback is not NULL, then -** sqlite3_exec() invokes the callback with three arguments. The -** second argument is the name of the locked table and the third -** argument is the number of times the table has been busy. If the -** busy callback returns 0, then sqlite3_exec() immediately returns -** SQLITE_BUSY. If the callback returns non-zero, then sqlite3_exec() -** tries to open the table again and the cycle repeats. -** -** The default busy callback is NULL. -** -** Sqlite is re-entrant, so the busy handler may start a new query. -** (It is not clear why anyone would every want to do this, but it -** is allowed, in theory.) But the busy handler may not close the -** database. Closing the database from a busy handler will delete -** data structures out from under the executing query and will -** probably result in a coredump. -*/ -int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); - -/* -** This routine sets a busy handler that sleeps for a while when a -** table is locked. The handler will sleep multiple times until -** at least "ms" milleseconds of sleeping have been done. After -** "ms" milleseconds of sleeping, the handler returns 0 which -** causes sqlite3_exec() to return SQLITE_BUSY. -** -** Calling this routine with an argument less than or equal to zero -** turns off all busy handlers. -*/ -int sqlite3_busy_timeout(sqlite3*, int ms); - -/* -** This next routine is really just a wrapper around sqlite3_exec(). -** Instead of invoking a user-supplied callback for each row of the -** result, this routine remembers each row of the result in memory -** obtained from malloc(), then returns all of the result after the -** query has finished. -** -** As an example, suppose the query result where this table: -** -** Name | Age -** ----------------------- -** Alice | 43 -** Bob | 28 -** Cindy | 21 -** -** If the 3rd argument were &azResult then after the function returns -** azResult will contain the following data: -** -** azResult[0] = "Name"; -** azResult[1] = "Age"; -** azResult[2] = "Alice"; -** azResult[3] = "43"; -** azResult[4] = "Bob"; -** azResult[5] = "28"; -** azResult[6] = "Cindy"; -** azResult[7] = "21"; -** -** Notice that there is an extra row of data containing the column -** headers. But the *nrow return value is still 3. *ncolumn is -** set to 2. In general, the number of values inserted into azResult -** will be ((*nrow) + 1)*(*ncolumn). -** -** After the calling function has finished using the result, it should -** pass the result data pointer to sqlite3_free_table() in order to -** release the memory that was malloc-ed. Because of the way the -** malloc() happens, the calling function must not try to call -** malloc() directly. Only sqlite3_free_table() is able to release -** the memory properly and safely. -** -** The return value of this routine is the same as from sqlite3_exec(). -*/ -int sqlite3_get_table( - sqlite3*, /* An open database */ - const char *sql, /* SQL to be executed */ - char ***resultp, /* Result written to a char *[] that this points to */ - int *nrow, /* Number of result rows written here */ - int *ncolumn, /* Number of result columns written here */ - char **errmsg /* Error msg written here */ -); - -/* -** Call this routine to free the memory that sqlite3_get_table() allocated. -*/ -void sqlite3_free_table(char **result); - -/* -** The following routines are variants of the "sprintf()" from the -** standard C library. The resulting string is written into memory -** obtained from malloc() so that there is never a possiblity of buffer -** overflow. These routines also implement some additional formatting -** options that are useful for constructing SQL statements. -** -** The strings returned by these routines should be freed by calling -** sqlite3_free(). -** -** All of the usual printf formatting options apply. In addition, there -** is a "%q" option. %q works like %s in that it substitutes a null-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal. By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, so some string variable contains text as follows: -** -** char *zText = "It's a happy day!"; -** -** We can use this text in an SQL statement as follows: -** -** sqlite3_exec_printf(db, "INSERT INTO table VALUES('%q')", -** callback1, 0, 0, zText); -** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -** INSERT INTO table1 VALUES('It''s a happy day!') -** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -** INSERT INTO table1 VALUES('It's a happy day!'); -** -** This second example is an SQL syntax error. As a general rule you -** should always use %q instead of %s when inserting text into a string -** literal. -*/ -char *sqlite3_mprintf(const char*,...); -char *sqlite3_vmprintf(const char*, va_list); -void sqlite3_free(char *z); -char *sqlite3_snprintf(int,char*,const char*, ...); - -#ifndef SQLITE_OMIT_AUTHORIZATION -/* -** This routine registers a callback with the SQLite library. The -** callback is invoked (at compile-time, not at run-time) for each -** attempt to access a column of a table in the database. The callback -** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire -** SQL statement should be aborted with an error and SQLITE_IGNORE -** if the column should be treated as a NULL value. -*/ -int sqlite3_set_authorizer( - sqlite3*, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pUserData -); -#endif - -/* -** The second parameter to the access authorization function above will -** be one of the values below. These values signify what kind of operation -** is to be authorized. The 3rd and 4th parameters to the authorization -** function will be parameters or NULL depending on which of the following -** codes is used as the second parameter. The 5th parameter is the name -** of the database ("main", "temp", etc.) if applicable. The 6th parameter -** is the name of the inner-most trigger or view that is responsible for -** the access attempt or NULL if this access attempt is directly from -** input SQL code. -** -** Arg-3 Arg-4 -*/ -#define SQLITE_COPY 0 /* Table Name File Name */ -#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ -#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ -#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ -#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ -#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ -#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ -#define SQLITE_DELETE 9 /* Table Name NULL */ -#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ -#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ -#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ -#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ -#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ -#define SQLITE_DROP_VIEW 17 /* View Name NULL */ -#define SQLITE_INSERT 18 /* Table Name NULL */ -#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ -#define SQLITE_READ 20 /* Table Name Column Name */ -#define SQLITE_SELECT 21 /* NULL NULL */ -#define SQLITE_TRANSACTION 22 /* NULL NULL */ -#define SQLITE_UPDATE 23 /* Table Name Column Name */ -#define SQLITE_ATTACH 24 /* Filename NULL */ -#define SQLITE_DETACH 25 /* Database Name NULL */ - - -/* -** The return value of the authorization function should be one of the -** following constants: -*/ -/* #define SQLITE_OK 0 // Allow access (This is actually defined above) */ -#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ -#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ - -/* -** Register a function that is called at every invocation of sqlite3_exec() -** or sqlite3_prepare(). This function can be used (for example) to generate -** a log file of all SQL executed against a database. -*/ -void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); - -/* -** This routine configures a callback function - the progress callback - that -** is invoked periodically during long running calls to sqlite3_exec(), -** sqlite3_step() and sqlite3_get_table(). An example use for this API is to keep -** a GUI updated during a large query. -** -** The progress callback is invoked once for every N virtual machine opcodes, -** where N is the second argument to this function. The progress callback -** itself is identified by the third argument to this function. The fourth -** argument to this function is a void pointer passed to the progress callback -** function each time it is invoked. -** -** If a call to sqlite3_exec(), sqlite3_step() or sqlite3_get_table() results -** in less than N opcodes being executed, then the progress callback is not -** invoked. -** -** To remove the progress callback altogether, pass NULL as the third -** argument to this function. -** -** If the progress callback returns a result other than 0, then the current -** query is immediately terminated and any database changes rolled back. If the -** query was part of a larger transaction, then the transaction is not rolled -** back and remains active. The sqlite3_exec() call returns SQLITE_ABORT. -** -******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** -*/ -void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -/* -** Register a callback function to be invoked whenever a new transaction -** is committed. The pArg argument is passed through to the callback. -** callback. If the callback function returns non-zero, then the commit -** is converted into a rollback. -** -** If another function was previously registered, its pArg value is returned. -** Otherwise NULL is returned. -** -** Registering a NULL function disables the callback. -** -******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** -*/ -void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); - -/* -** Open the sqlite database file "filename". The "filename" is UTF-8 -** encoded for sqlite3_open() and UTF-16 encoded in the native byte order -** for sqlite3_open16(). An sqlite3* handle is returned in *ppDb, even -** if an error occurs. If the database is opened (or created) successfully, -** then SQLITE_OK is returned. Otherwise an error code is returned. The -** sqlite3_errmsg() or sqlite3_errmsg16() routines can be used to obtain -** an English language description of the error. -** -** If the database file does not exist, then a new database is created. -** The encoding for the database is UTF-8 if sqlite3_open() is called and -** UTF-16 if sqlite3_open16 is used. -** -** Whether or not an error occurs when it is opened, resources associated -** with the sqlite3* handle should be released by passing it to -** sqlite3_close() when it is no longer required. -*/ -int sqlite3_open( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -int sqlite3_open16( - const void *filename, /* Database filename (UTF-16) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); - -/* -** Return the error code for the most recent sqlite3_* API call associated -** with sqlite3 handle 'db'. SQLITE_OK is returned if the most recent -** API call was successful. -** -** Calls to many sqlite3_* functions set the error code and string returned -** by sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16() -** (overwriting the previous values). Note that calls to sqlite3_errcode(), -** sqlite3_errmsg() and sqlite3_errmsg16() themselves do not affect the -** results of future invocations. -** -** Assuming no other intervening sqlite3_* API calls are made, the error -** code returned by this function is associated with the same error as -** the strings returned by sqlite3_errmsg() and sqlite3_errmsg16(). -*/ -int sqlite3_errcode(sqlite3 *db); - -/* -** Return a pointer to a UTF-8 encoded string describing in english the -** error condition for the most recent sqlite3_* API call. The returned -** string is always terminated by an 0x00 byte. -** -** The string "not an error" is returned when the most recent API call was -** successful. -*/ -const char *sqlite3_errmsg(sqlite3*); - -/* -** Return a pointer to a UTF-16 native byte order encoded string describing -** in english the error condition for the most recent sqlite3_* API call. -** The returned string is always terminated by a pair of 0x00 bytes. -** -** The string "not an error" is returned when the most recent API call was -** successful. -*/ -const void *sqlite3_errmsg16(sqlite3*); - -/* -** An instance of the following opaque structure is used to represent -** a compiled SQL statment. -*/ -typedef struct sqlite3_stmt sqlite3_stmt; - -/* -** To execute an SQL query, it must first be compiled into a byte-code -** program using one of the following routines. The only difference between -** them is that the second argument, specifying the SQL statement to -** compile, is assumed to be encoded in UTF-8 for the sqlite3_prepare() -** function and UTF-16 for sqlite3_prepare16(). -** -** The first parameter "db" is an SQLite database handle. The second -** parameter "zSql" is the statement to be compiled, encoded as either -** UTF-8 or UTF-16 (see above). If the next parameter, "nBytes", is less -** than zero, then zSql is read up to the first nul terminator. If -** "nBytes" is not less than zero, then it is the length of the string zSql -** in bytes (not characters). -** -** *pzTail is made to point to the first byte past the end of the first -** SQL statement in zSql. This routine only compiles the first statement -** in zSql, so *pzTail is left pointing to what remains uncompiled. -** -** *ppStmt is left pointing to a compiled SQL statement that can be -** executed using sqlite3_step(). Or if there is an error, *ppStmt may be -** set to NULL. If the input text contained no SQL (if the input is and -** empty string or a comment) then *ppStmt is set to NULL. -** -** On success, SQLITE_OK is returned. Otherwise an error code is returned. -*/ -int sqlite3_prepare( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -int sqlite3_prepare16( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); - -/* -** Pointers to the following two opaque structures are used to communicate -** with the implementations of user-defined functions. -*/ -typedef struct sqlite3_context sqlite3_context; -typedef struct Mem sqlite3_value; - -/* -** In the SQL strings input to sqlite3_prepare() and sqlite3_prepare16(), -** one or more literals can be replace by a wildcard "?" or ":N:" where -** N is an integer. These value of these wildcard literals can be set -** using the routines listed below. -** -** In every case, the first parameter is a pointer to the sqlite3_stmt -** structure returned from sqlite3_prepare(). The second parameter is the -** index of the wildcard. The first "?" has an index of 1. ":N:" wildcards -** use the index N. -** -** The fifth parameter to sqlite3_bind_blob(), sqlite3_bind_text(), and -** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** text after SQLite has finished with it. If the fifth argument is the -** special value SQLITE_STATIC, then the library assumes that the information -** is in static, unmanaged space and does not need to be freed. If the -** fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its -** own private copy of the data. -** -** The sqlite3_bind_* routine must be called before sqlite3_step() after -** an sqlite3_prepare() or sqlite3_reset(). Unbound wildcards are interpreted -** as NULL. -*/ -int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -int sqlite3_bind_double(sqlite3_stmt*, int, double); -int sqlite3_bind_int(sqlite3_stmt*, int, int); -int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite_int64); -int sqlite3_bind_null(sqlite3_stmt*, int); -int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); -int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); - -/* -** Return the number of wildcards in a compiled SQL statement. This -** routine was added to support DBD::SQLite. -*/ -int sqlite3_bind_parameter_count(sqlite3_stmt*); - -/* -** Return the name of the i-th parameter. Ordinary wildcards "?" are -** nameless and a NULL is returned. For wildcards of the form :N or -** $vvvv the complete text of the wildcard is returned. -** NULL is returned if the index is out of range. -*/ -const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); - -/* -** Return the index of a parameter with the given name. The name -** must match exactly. If no parameter with the given name is found, -** return 0. -*/ -int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); - -/* -** Return the number of columns in the result set returned by the compiled -** SQL statement. This routine returns 0 if pStmt is an SQL statement -** that does not return data (for example an UPDATE). -*/ -int sqlite3_column_count(sqlite3_stmt *pStmt); - -/* -** The first parameter is a compiled SQL statement. This function returns -** the column heading for the Nth column of that statement, where N is the -** second function parameter. The string returned is UTF-8 for -** sqlite3_column_name() and UTF-16 for sqlite3_column_name16(). -*/ -const char *sqlite3_column_name(sqlite3_stmt*,int); -const void *sqlite3_column_name16(sqlite3_stmt*,int); - -/* -** The first parameter is a compiled SQL statement. If this statement -** is a SELECT statement, the Nth column of the returned result set -** of the SELECT is a table column then the declared type of the table -** column is returned. If the Nth column of the result set is not at table -** column, then a NULL pointer is returned. The returned string is always -** UTF-8 encoded. For example, in the database schema: -** -** CREATE TABLE t1(c1 VARIANT); -** -** And the following statement compiled: -** -** SELECT c1 + 1, 0 FROM t1; -** -** Then this routine would return the string "VARIANT" for the second -** result column (i==1), and a NULL pointer for the first result column -** (i==0). -*/ -const char *sqlite3_column_decltype(sqlite3_stmt *, int i); - -/* -** The first parameter is a compiled SQL statement. If this statement -** is a SELECT statement, the Nth column of the returned result set -** of the SELECT is a table column then the declared type of the table -** column is returned. If the Nth column of the result set is not at table -** column, then a NULL pointer is returned. The returned string is always -** UTF-16 encoded. For example, in the database schema: -** -** CREATE TABLE t1(c1 INTEGER); -** -** And the following statement compiled: -** -** SELECT c1 + 1, 0 FROM t1; -** -** Then this routine would return the string "INTEGER" for the second -** result column (i==1), and a NULL pointer for the first result column -** (i==0). -*/ -const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - -/* -** After an SQL query has been compiled with a call to either -** sqlite3_prepare() or sqlite3_prepare16(), then this function must be -** called one or more times to execute the statement. -** -** The return value will be either SQLITE_BUSY, SQLITE_DONE, -** SQLITE_ROW, SQLITE_ERROR, or SQLITE_MISUSE. -** -** SQLITE_BUSY means that the database engine attempted to open -** a locked database and there is no busy callback registered. -** Call sqlite3_step() again to retry the open. -** -** SQLITE_DONE means that the statement has finished executing -** successfully. sqlite3_step() should not be called again on this virtual -** machine. -** -** If the SQL statement being executed returns any data, then -** SQLITE_ROW is returned each time a new row of data is ready -** for processing by the caller. The values may be accessed using -** the sqlite3_column_*() functions described below. sqlite3_step() -** is called again to retrieve the next row of data. -** -** SQLITE_ERROR means that a run-time error (such as a constraint -** violation) has occurred. sqlite3_step() should not be called again on -** the VM. More information may be found by calling sqlite3_errmsg(). -** -** SQLITE_MISUSE means that the this routine was called inappropriately. -** Perhaps it was called on a virtual machine that had already been -** finalized or on one that had previously returned SQLITE_ERROR or -** SQLITE_DONE. Or it could be the case the the same database connection -** is being used simulataneously by two or more threads. -*/ -int sqlite3_step(sqlite3_stmt*); - -/* -** Return the number of values in the current row of the result set. -** -** After a call to sqlite3_step() that returns SQLITE_ROW, this routine -** will return the same value as the sqlite3_column_count() function. -** After sqlite3_step() has returned an SQLITE_DONE, SQLITE_BUSY or -** error code, or before sqlite3_step() has been called on a -** compiled SQL statement, this routine returns zero. -*/ -int sqlite3_data_count(sqlite3_stmt *pStmt); - -/* -** Values are stored in the database in one of the following fundamental -** types. -*/ -#define SQLITE_INTEGER 1 -#define SQLITE_FLOAT 2 -/* #define SQLITE_TEXT 3 // See below */ -#define SQLITE_BLOB 4 -#define SQLITE_NULL 5 - -/* -** SQLite version 2 defines SQLITE_TEXT differently. To allow both -** version 2 and version 3 to be included, undefine them both if a -** conflict is seen. Define SQLITE3_TEXT to be the version 3 value. -*/ -#ifdef SQLITE_TEXT -# undef SQLITE_TEXT -#else -# define SQLITE_TEXT 3 -#endif -#define SQLITE3_TEXT 3 - -/* -** The next group of routines returns information about the information -** in a single column of the current result row of a query. In every -** case the first parameter is a pointer to the SQL statement that is being -** executed (the sqlite_stmt* that was returned from sqlite3_prepare()) and -** the second argument is the index of the column for which information -** should be returned. iCol is zero-indexed. The left-most column as an -** index of 0. -** -** If the SQL statement is not currently point to a valid row, or if the -** the colulmn index is out of range, the result is undefined. -** -** These routines attempt to convert the value where appropriate. For -** example, if the internal representation is FLOAT and a text result -** is requested, sprintf() is used internally to do the conversion -** automatically. The following table details the conversions that -** are applied: -** -** Internal Type Requested Type Conversion -** ------------- -------------- -------------------------- -** NULL INTEGER Result is 0 -** NULL FLOAT Result is 0.0 -** NULL TEXT Result is an empty string -** NULL BLOB Result is a zero-length BLOB -** INTEGER FLOAT Convert from integer to float -** INTEGER TEXT ASCII rendering of the integer -** INTEGER BLOB Same as for INTEGER->TEXT -** FLOAT INTEGER Convert from float to integer -** FLOAT TEXT ASCII rendering of the float -** FLOAT BLOB Same as FLOAT->TEXT -** TEXT INTEGER Use atoi() -** TEXT FLOAT Use atof() -** TEXT BLOB No change -** BLOB INTEGER Convert to TEXT then use atoi() -** BLOB FLOAT Convert to TEXT then use atof() -** BLOB TEXT Add a \000 terminator if needed -** -** The following access routines are provided: -** -** _type() Return the datatype of the result. This is one of -** SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, -** or SQLITE_NULL. -** _blob() Return the value of a BLOB. -** _bytes() Return the number of bytes in a BLOB value or the number -** of bytes in a TEXT value represented as UTF-8. The \000 -** terminator is included in the byte count for TEXT values. -** _bytes16() Return the number of bytes in a BLOB value or the number -** of bytes in a TEXT value represented as UTF-16. The \u0000 -** terminator is included in the byte count for TEXT values. -** _double() Return a FLOAT value. -** _int() Return an INTEGER value in the host computer's native -** integer representation. This might be either a 32- or 64-bit -** integer depending on the host. -** _int64() Return an INTEGER value as a 64-bit signed integer. -** _text() Return the value as UTF-8 text. -** _text16() Return the value as UTF-16 text. -*/ -const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -double sqlite3_column_double(sqlite3_stmt*, int iCol); -int sqlite3_column_int(sqlite3_stmt*, int iCol); -sqlite_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -int sqlite3_column_type(sqlite3_stmt*, int iCol); - -/* -** The sqlite3_finalize() function is called to delete a compiled -** SQL statement obtained by a previous call to sqlite3_prepare() -** or sqlite3_prepare16(). If the statement was executed successfully, or -** not executed at all, then SQLITE_OK is returned. If execution of the -** statement failed then an error code is returned. -** -** This routine can be called at any point during the execution of the -** virtual machine. If the virtual machine has not completed execution -** when this routine is called, that is like encountering an error or -** an interrupt. (See sqlite3_interrupt().) Incomplete updates may be -** rolled back and transactions cancelled, depending on the circumstances, -** and the result code returned will be SQLITE_ABORT. -*/ -int sqlite3_finalize(sqlite3_stmt *pStmt); - -/* -** The sqlite3_reset() function is called to reset a compiled SQL -** statement obtained by a previous call to sqlite3_prepare() or -** sqlite3_prepare16() back to it's initial state, ready to be re-executed. -** Any SQL statement variables that had values bound to them using -** the sqlite3_bind_*() API retain their values. -*/ -int sqlite3_reset(sqlite3_stmt *pStmt); - -/* -** The following two functions are used to add user functions or aggregates -** implemented in C to the SQL langauge interpreted by SQLite. The -** difference only between the two is that the second parameter, the -** name of the (scalar) function or aggregate, is encoded in UTF-8 for -** sqlite3_create_function() and UTF-16 for sqlite3_create_function16(). -** -** The first argument is the database handle that the new function or -** aggregate is to be added to. If a single program uses more than one -** database handle internally, then user functions or aggregates must -** be added individually to each database handle with which they will be -** used. -** -** The third parameter is the number of arguments that the function or -** aggregate takes. If this parameter is negative, then the function or -** aggregate may take any number of arguments. -** -** The fourth parameter is one of SQLITE_UTF* values defined below, -** indicating the encoding that the function is most likely to handle -** values in. This does not change the behaviour of the programming -** interface. However, if two versions of the same function are registered -** with different encoding values, SQLite invokes the version likely to -** minimize conversions between text encodings. -** -** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are -** pointers to user implemented C functions that implement the user -** function or aggregate. A scalar function requires an implementation of -** the xFunc callback only, NULL pointers should be passed as the xStep -** and xFinal parameters. An aggregate function requires an implementation -** of xStep and xFinal, but NULL should be passed for xFunc. To delete an -** existing user function or aggregate, pass NULL for all three function -** callback. Specifying an inconstent set of callback values, such as an -** xFunc and an xFinal, or an xStep but no xFinal, SQLITE_ERROR is -** returned. -*/ -int sqlite3_create_function( - sqlite3 *, - const char *zFunctionName, - int nArg, - int eTextRep, - void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -int sqlite3_create_function16( - sqlite3*, - const void *zFunctionName, - int nArg, - int eTextRep, - void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); - -/* -** The next routine returns the number of calls to xStep for a particular -** aggregate function instance. The current call to xStep counts so this -** routine always returns at least 1. -*/ -int sqlite3_aggregate_count(sqlite3_context*); - -/* -** The next group of routines returns information about parameters to -** a user-defined function. Function implementations use these routines -** to access their parameters. These routines are the same as the -** sqlite3_column_* routines except that these routines take a single -** sqlite3_value* pointer instead of an sqlite3_stmt* and an integer -** column number. -*/ -const void *sqlite3_value_blob(sqlite3_value*); -int sqlite3_value_bytes(sqlite3_value*); -int sqlite3_value_bytes16(sqlite3_value*); -double sqlite3_value_double(sqlite3_value*); -int sqlite3_value_int(sqlite3_value*); -sqlite_int64 sqlite3_value_int64(sqlite3_value*); -const unsigned char *sqlite3_value_text(sqlite3_value*); -const void *sqlite3_value_text16(sqlite3_value*); -const void *sqlite3_value_text16le(sqlite3_value*); -const void *sqlite3_value_text16be(sqlite3_value*); -int sqlite3_value_type(sqlite3_value*); - -/* -** Aggregate functions use the following routine to allocate -** a structure for storing their state. The first time this routine -** is called for a particular aggregate, a new structure of size nBytes -** is allocated, zeroed, and returned. On subsequent calls (for the -** same aggregate instance) the same buffer is returned. The implementation -** of the aggregate can use the returned buffer to accumulate data. -** -** The buffer allocated is freed automatically by SQLite. -*/ -void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); - -/* -** The pUserData parameter to the sqlite3_create_function() and -** sqlite3_create_aggregate() routines used to register user functions -** is available to the implementation of the function using this -** call. -*/ -void *sqlite3_user_data(sqlite3_context*); - -/* -** The following two functions may be used by scalar user functions to -** associate meta-data with argument values. If the same value is passed to -** multiple invocations of the user-function during query execution, under -** some circumstances the associated meta-data may be preserved. This may -** be used, for example, to add a regular-expression matching scalar -** function. The compiled version of the regular expression is stored as -** meta-data associated with the SQL value passed as the regular expression -** pattern. -** -** Calling sqlite3_get_auxdata() returns a pointer to the meta data -** associated with the Nth argument value to the current user function -** call, where N is the second parameter. If no meta-data has been set for -** that value, then a NULL pointer is returned. -** -** The sqlite3_set_auxdata() is used to associate meta data with a user -** function argument. The third parameter is a pointer to the meta data -** to be associated with the Nth user function argument value. The fourth -** parameter specifies a 'delete function' that will be called on the meta -** data pointer to release it when it is no longer required. If the delete -** function pointer is NULL, it is not invoked. -** -** In practice, meta-data is preserved between function calls for -** expressions that are constant at compile time. This includes literal -** values and SQL variables. -*/ -void *sqlite3_get_auxdata(sqlite3_context*, int); -void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); - - -/* -** These are special value for the destructor that is passed in as the -** final argument to routines like sqlite3_result_blob(). If the destructor -** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. The -** SQLITE_TRANSIENT value means that the content will likely change in -** the near future and that SQLite should make its own private copy of -** the content before returning. -*/ -#define SQLITE_STATIC ((void(*)(void *))0) -#define SQLITE_TRANSIENT ((void(*)(void *))-1) - -/* -** User-defined functions invoke the following routines in order to -** set their return value. -*/ -void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -void sqlite3_result_double(sqlite3_context*, double); -void sqlite3_result_error(sqlite3_context*, const char*, int); -void sqlite3_result_error16(sqlite3_context*, const void*, int); -void sqlite3_result_int(sqlite3_context*, int); -void sqlite3_result_int64(sqlite3_context*, sqlite_int64); -void sqlite3_result_null(sqlite3_context*); -void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -void sqlite3_result_value(sqlite3_context*, sqlite3_value*); - -/* -** These are the allowed values for the eTextRep argument to -** sqlite3_create_collation and sqlite3_create_function. -*/ -#define SQLITE_UTF8 1 -#define SQLITE_UTF16LE 2 -#define SQLITE_UTF16BE 3 -#define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* sqlite3_create_function only */ - -/* -** These two functions are used to add new collation sequences to the -** sqlite3 handle specified as the first argument. -** -** The name of the new collation sequence is specified as a UTF-8 string -** for sqlite3_create_collation() and a UTF-16 string for -** sqlite3_create_collation16(). In both cases the name is passed as the -** second function argument. -** -** The third argument must be one of the constants SQLITE_UTF8, -** SQLITE_UTF16LE or SQLITE_UTF16BE, indicating that the user-supplied -** routine expects to be passed pointers to strings encoded using UTF-8, -** UTF-16 little-endian or UTF-16 big-endian respectively. -** -** A pointer to the user supplied routine must be passed as the fifth -** argument. If it is NULL, this is the same as deleting the collation -** sequence (so that SQLite cannot call it anymore). Each time the user -** supplied function is invoked, it is passed a copy of the void* passed as -** the fourth argument to sqlite3_create_collation() or -** sqlite3_create_collation16() as its first parameter. -** -** The remaining arguments to the user-supplied routine are two strings, -** each represented by a [length, data] pair and encoded in the encoding -** that was passed as the third argument when the collation sequence was -** registered. The user routine should return negative, zero or positive if -** the first string is less than, equal to, or greater than the second -** string. i.e. (STRING1 - STRING2). -*/ -int sqlite3_create_collation( - sqlite3*, - const char *zName, - int eTextRep, - void*, - int(*xCompare)(void*,int,const void*,int,const void*) -); -int sqlite3_create_collation16( - sqlite3*, - const char *zName, - int eTextRep, - void*, - int(*xCompare)(void*,int,const void*,int,const void*) -); - -/* -** To avoid having to register all collation sequences before a database -** can be used, a single callback function may be registered with the -** database handle to be called whenever an undefined collation sequence is -** required. -** -** If the function is registered using the sqlite3_collation_needed() API, -** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. If sqlite3_collation_needed16() is used, the names -** are passed as UTF-16 in machine native byte order. A call to either -** function replaces any existing callback. -** -** When the user-function is invoked, the first argument passed is a copy -** of the second argument to sqlite3_collation_needed() or -** sqlite3_collation_needed16(). The second argument is the database -** handle. The third argument is one of SQLITE_UTF8, SQLITE_UTF16BE or -** SQLITE_UTF16LE, indicating the most desirable form of the collation -** sequence function required. The fourth parameter is the name of the -** required collation sequence. -** -** The collation sequence is returned to SQLite by a collation-needed -** callback using the sqlite3_create_collation() or -** sqlite3_create_collation16() APIs, described above. -*/ -int sqlite3_collation_needed( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const char*) -); -int sqlite3_collation_needed16( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const void*) -); - -/* -** Specify the key for an encrypted database. This routine should be -** called right after sqlite3_open(). -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -int sqlite3_key( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The key */ -); - -/* -** Change the key on an open database. If the current database is not -** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the -** database is decrypted. -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -int sqlite3_rekey( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The new key */ -); - -/* -** If the following global variable is made to point to a constant -** string which is the name of a directory, then all temporary files -** created by SQLite will be placed in that directory. If this variable -** is NULL pointer, then SQLite does a search for an appropriate temporary -** file directory. -** -** This variable should only be changed when there are no open databases. -** Once sqlite3_open() has been called, this variable should not be changed -** until all database connections are closed. -*/ -extern const char *sqlite3_temp_directory; - -#ifdef __cplusplus -} /* End of the 'extern "C"' block */ -#endif -#endif diff --git a/kopete/plugins/statistics/sqlite/sqliteInt.h b/kopete/plugins/statistics/sqlite/sqliteInt.h deleted file mode 100644 index b4fa474b..00000000 --- a/kopete/plugins/statistics/sqlite/sqliteInt.h +++ /dev/null @@ -1,1419 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Internal interface definitions for SQLite. -** -** @(#) $Id$ -*/ -#ifndef _SQLITEINT_H_ -#define _SQLITEINT_H_ - -/* -** These #defines should enable >2GB file support on Posix if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: RedHat 7.2) but you want your code to work -** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in RedHat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for MacOS. LFS is only supported on MacOS 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -#include "config.h" -#include "sqlite3.h" -#include "hash.h" -#include "parse.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -/* -** The maximum number of in-memory pages to use for the main database -** table and for temporary tables. -*/ -#define MAX_PAGES 2000 -#define TEMP_PAGES 500 - -/* -** If the following macro is set to 1, then NULL values are considered -** distinct for the SELECT DISTINCT statement and for UNION or EXCEPT -** compound queries. No other SQL database engine (among those tested) -** works this way except for OCELOT. But the SQL92 spec implies that -** this is how things should work. -** -** If the following macro is set to 0, then NULLs are indistinct for -** SELECT DISTINCT and for UNION. -*/ -#define NULL_ALWAYS_DISTINCT 0 - -/* -** If the following macro is set to 1, then NULL values are considered -** distinct when determining whether or not two entries are the same -** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL, -** OCELOT, and Firebird all work. The SQL92 spec explicitly says this -** is the way things are suppose to work. -** -** If the following macro is set to 0, the NULLs are indistinct for -** a UNIQUE index. In this mode, you can only have a single NULL entry -** for a column declared UNIQUE. This is the way Informix and SQL Server -** work. -*/ -#define NULL_DISTINCT_FOR_UNIQUE 1 - -/* -** The maximum number of attached databases. This must be at least 2 -** in order to support the main database file (0) and the file used to -** hold temporary tables (1). And it must be less than 32 because -** we use a bitmask of databases with a u32 in places (for example -** the Parse.cookieMask field). -*/ -#define MAX_ATTACHED 10 - -/* -** The maximum value of a ?nnn wildcard that the parser will accept. -*/ -#define SQLITE_MAX_VARIABLE_NUMBER 999 - -/* -** When building SQLite for embedded systems where memory is scarce, -** you can define one or more of the following macros to omit extra -** features of the library and thus keep the size of the library to -** a minimum. -*/ -/* #define SQLITE_OMIT_AUTHORIZATION 1 */ -/* #define SQLITE_OMIT_INMEMORYDB 1 */ -/* #define SQLITE_OMIT_VACUUM 1 */ -/* #define SQLITE_OMIT_DATETIME_FUNCS 1 */ -/* #define SQLITE_OMIT_PROGRESS_CALLBACK 1 */ - -/* -** Integers of known sizes. These typedefs might change for architectures -** where the sizes very. Preprocessor macros are available so that the -** types can be conveniently redefined at compile-type. Like this: -** -** cc '-DUINTPTR_TYPE=long long int' ... -*/ -#ifndef UINT64_TYPE -# if defined(_MSC_VER) || defined(__BORLANDC__) -# define UINT64_TYPE unsigned __int64 -# else -# define UINT64_TYPE unsigned long long int -# endif -#endif -#ifndef UINT32_TYPE -# define UINT32_TYPE unsigned int -#endif -#ifndef UINT16_TYPE -# define UINT16_TYPE unsigned short int -#endif -#ifndef INT16_TYPE -# define INT16_TYPE short int -#endif -#ifndef UINT8_TYPE -# define UINT8_TYPE unsigned char -#endif -#ifndef INT8_TYPE -# define INT8_TYPE signed char -#endif -#ifndef LONGDOUBLE_TYPE -# define LONGDOUBLE_TYPE long double -#endif -#ifndef INTPTR_TYPE -# if SQLITE_PTR_SZ==4 -# define INTPTR_TYPE int -# else -# define INTPTR_TYPE sqlite_int64 -# endif -#endif -#ifndef UINTPTR_TYPE -# if SQLITE_PTR_SZ==4 -# define UINTPTR_TYPE unsigned int -# else -# define UINTPTR_TYPE sqlite_uint64 -# endif -#endif -typedef sqlite_int64 i64; /* 8-byte signed integer */ -typedef UINT64_TYPE u64; /* 8-byte unsigned integer */ -typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ -typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ -typedef INT16_TYPE i16; /* 2-byte signed integer */ -typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ -typedef UINT8_TYPE i8; /* 1-byte signed integer */ -typedef INTPTR_TYPE ptr; /* Big enough to hold a pointer */ -typedef UINTPTR_TYPE uptr; /* Big enough to hold a pointer */ - -/* -** Macros to determine whether the machine is big or little endian, -** evaluated at runtime. -*/ -extern const int sqlite3one; -#define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) -#define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) - -/* -** An instance of the following structure is used to store the busy-handler -** callback for a given sqlite handle. -** -** The sqlite.busyHandler member of the sqlite struct contains the busy -** callback for the database handle. Each pager opened via the sqlite -** handle is passed a pointer to sqlite.busyHandler. The busy-handler -** callback is currently invoked only from within pager.c. -*/ -typedef struct BusyHandler BusyHandler; -struct BusyHandler { - int (*xFunc)(void *,int); /* The busy callback */ - void *pArg; /* First arg to busy callback */ -}; - -/* -** Defer sourcing vdbe.h and btree.h until after the "u8" and -** "BusyHandler typedefs. -*/ -#include "vdbe.h" -#include "btree.h" - -/* -** This macro casts a pointer to an integer. Useful for doing -** pointer arithmetic. -*/ -#define Addr(X) ((uptr)X) - -/* -** If memory allocation problems are found, recompile with -** -** -DSQLITE_DEBUG=1 -** -** to enable some sanity checking on malloc() and free(). To -** check for memory leaks, recompile with -** -** -DSQLITE_DEBUG=2 -** -** and a line of text will be written to standard error for -** each malloc() and free(). This output can be analyzed -** by an AWK script to determine if there are any leaks. -*/ -#ifdef SQLITE_DEBUG -# define sqliteMalloc(X) sqlite3Malloc_(X,1,__FILE__,__LINE__) -# define sqliteMallocRaw(X) sqlite3Malloc_(X,0,__FILE__,__LINE__) -# define sqliteFree(X) sqlite3Free_(X,__FILE__,__LINE__) -# define sqliteRealloc(X,Y) sqlite3Realloc_(X,Y,__FILE__,__LINE__) -# define sqliteStrDup(X) sqlite3StrDup_(X,__FILE__,__LINE__) -# define sqliteStrNDup(X,Y) sqlite3StrNDup_(X,Y,__FILE__,__LINE__) -#else -# define sqliteFree sqlite3FreeX -# define sqliteMalloc sqlite3Malloc -# define sqliteMallocRaw sqlite3MallocRaw -# define sqliteRealloc sqlite3Realloc -# define sqliteStrDup sqlite3StrDup -# define sqliteStrNDup sqlite3StrNDup -#endif - -/* -** This variable gets set if malloc() ever fails. After it gets set, -** the SQLite library shuts down permanently. -*/ -extern int sqlite3_malloc_failed; - -/* -** The following global variables are used for testing and debugging -** only. They only work if SQLITE_DEBUG is defined. -*/ -#ifdef SQLITE_DEBUG -extern int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */ -extern int sqlite3_nFree; /* Number of sqliteFree() calls */ -extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */ -#endif - -/* -** Name of the master database table. The master database table -** is a special table that holds the names and attributes of all -** user tables and indices. -*/ -#define MASTER_NAME "sqlite_master" -#define TEMP_MASTER_NAME "sqlite_temp_master" - -/* -** The root-page of the master database table. -*/ -#define MASTER_ROOT 1 - -/* -** The name of the schema table. -*/ -#define SCHEMA_TABLE(x) (x==1?TEMP_MASTER_NAME:MASTER_NAME) - -/* -** A convenience macro that returns the number of elements in -** an array. -*/ -#define ArraySize(X) (sizeof(X)/sizeof(X[0])) - -/* -** Forward references to structures -*/ -typedef struct Column Column; -typedef struct Table Table; -typedef struct Index Index; -typedef struct Instruction Instruction; -typedef struct Expr Expr; -typedef struct ExprList ExprList; -typedef struct Parse Parse; -typedef struct Token Token; -typedef struct IdList IdList; -typedef struct SrcList SrcList; -typedef struct WhereInfo WhereInfo; -typedef struct WhereLevel WhereLevel; -typedef struct Select Select; -typedef struct AggExpr AggExpr; -typedef struct FuncDef FuncDef; -typedef struct Trigger Trigger; -typedef struct TriggerStep TriggerStep; -typedef struct TriggerStack TriggerStack; -typedef struct FKey FKey; -typedef struct Db Db; -typedef struct AuthContext AuthContext; -typedef struct KeyClass KeyClass; -typedef struct CollSeq CollSeq; -typedef struct KeyInfo KeyInfo; - -/* -** Each database file to be accessed by the system is an instance -** of the following structure. There are normally two of these structures -** in the sqlite.aDb[] array. aDb[0] is the main database file and -** aDb[1] is the database file used to hold temporary tables. Additional -** databases may be attached. -*/ -struct Db { - char *zName; /* Name of this database */ - Btree *pBt; /* The B*Tree structure for this database file */ - int schema_cookie; /* Database schema version number for this file */ - Hash tblHash; /* All tables indexed by name */ - Hash idxHash; /* All (named) indices indexed by name */ - Hash trigHash; /* All triggers indexed by name */ - Hash aFKey; /* Foreign keys indexed by to-table */ - u16 flags; /* Flags associated with this database */ - u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ - u8 safety_level; /* How aggressive at synching data to disk */ - int cache_size; /* Number of pages to use in the cache */ - void *pAux; /* Auxiliary data. Usually NULL */ - void (*xFreeAux)(void*); /* Routine to free pAux */ -}; - -/* -** These macros can be used to test, set, or clear bits in the -** Db.flags field. -*/ -#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P)) -#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0) -#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P) -#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P) - -/* -** Allowed values for the DB.flags field. -** -** The DB_SchemaLoaded flag is set after the database schema has been -** read into internal hash tables. -** -** DB_UnresetViews means that one or more views have column names that -** have been filled out. If the schema changes, these column names might -** changes and so the view will need to be reset. -*/ -#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ -#define DB_UnresetViews 0x0002 /* Some views have defined column names */ - -#define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) - -/* -** Each database is an instance of the following structure. -** -** The sqlite.lastRowid records the last insert rowid generated by an -** insert statement. Inserts on views do not affect its value. Each -** trigger has its own context, so that lastRowid can be updated inside -** triggers as usual. The previous value will be restored once the trigger -** exits. Upon entering a before or instead of trigger, lastRowid is no -** longer (since after version 2.8.12) reset to -1. -** -** The sqlite.nChange does not count changes within triggers and keeps no -** context. It is reset at start of sqlite3_exec. -** The sqlite.lsChange represents the number of changes made by the last -** insert, update, or delete statement. It remains constant throughout the -** length of a statement and is then updated by OP_SetCounts. It keeps a -** context stack just like lastRowid so that the count of changes -** within a trigger is not seen outside the trigger. Changes to views do not -** affect the value of lsChange. -** The sqlite.csChange keeps track of the number of current changes (since -** the last statement) and is used to update sqlite_lsChange. -** -** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 -** store the most recent error code and, if applicable, string. The -** internal function sqlite3Error() is used to set these variables -** consistently. -*/ -struct sqlite3 { - int nDb; /* Number of backends currently in use */ - Db *aDb; /* All backends */ - Db aDbStatic[2]; /* Static space for the 2 default backends */ - int flags; /* Miscellanous flags. See below */ - u8 file_format; /* What file format version is this database? */ - u8 temp_store; /* 1: file 2: memory 0: default */ - int nTable; /* Number of tables in the database */ - BusyHandler busyHandler; /* Busy callback */ - void *pCommitArg; /* Argument to xCommitCallback() */ - int (*xCommitCallback)(void*);/* Invoked at every commit. */ - Hash aFunc; /* All functions that can be in SQL exprs */ - Hash aCollSeq; /* All collating sequences */ - CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ - i64 lastRowid; /* ROWID of most recent insert (see above) */ - i64 priorNewRowid; /* Last randomly generated ROWID */ - int magic; /* Magic number for detect library misuse */ - int nChange; /* Value returned by sqlite3_changes() */ - int nTotalChange; /* Value returned by sqlite3_total_changes() */ - struct sqlite3InitInfo { /* Information used during initialization */ - int iDb; /* When back is being initialized */ - int newTnum; /* Rootpage of table being initialized */ - u8 busy; /* TRUE if currently initializing */ - } init; - struct Vdbe *pVdbe; /* List of active virtual machines */ - int activeVdbeCnt; /* Number of vdbes currently executing */ - void (*xTrace)(void*,const char*); /* Trace function */ - void *pTraceArg; /* Argument to the trace function */ -#ifndef SQLITE_OMIT_AUTHORIZATION - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); - /* Access authorization function */ - void *pAuthArg; /* 1st argument to the access auth function */ -#endif -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - int (*xProgress)(void *); /* The progress callback */ - void *pProgressArg; /* Argument to the progress callback */ - int nProgressOps; /* Number of opcodes for progress callback */ -#endif - - int errCode; /* Most recent error code (SQLITE_*) */ - u8 enc; /* Text encoding for this database. */ - u8 autoCommit; /* The auto-commit flag. */ - void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); - void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); - void *pCollNeededArg; - sqlite3_value *pValue; /* Value used for transient conversions */ - sqlite3_value *pErr; /* Most recent error message */ - - char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ - char *zErrMsg16; /* Most recent error message (UTF-8 encoded) */ -}; - -/* -** Possible values for the sqlite.flags and or Db.flags fields. -** -** On sqlite.flags, the SQLITE_InTrans value means that we have -** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement -** transaction is active on that particular database file. -*/ -#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ -#define SQLITE_Initialized 0x00000002 /* True after initialization */ -#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */ -#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ -#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ -#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */ -#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ -#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ - /* DELETE, or UPDATE and return */ - /* the count using a callback. */ -#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ - /* result set is empty */ -#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */ -#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */ - -/* -** Possible values for the sqlite.magic field. -** The numbers are obtained at random and have no special meaning, other -** than being distinct from one another. -*/ -#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ -#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ -#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ -#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ - -/* -** Each SQL function is defined by an instance of the following -** structure. A pointer to this structure is stored in the sqlite.aFunc -** hash table. When multiple functions have the same name, the hash table -** points to a linked list of these structures. -*/ -struct FuncDef { - char *zName; /* SQL name of the function */ - int nArg; /* Number of arguments. -1 means unlimited */ - u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ - void *pUserData; /* User data parameter */ - FuncDef *pNext; /* Next function with same name */ - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ - void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ - void (*xFinalize)(sqlite3_context*); /* Aggregate finializer */ - u8 needCollSeq; /* True if sqlite3GetFuncCollSeq() might be called */ -}; - -/* -** information about each column of an SQL table is held in an instance -** of this structure. -*/ -struct Column { - char *zName; /* Name of this column */ - char *zDflt; /* Default value of this column */ - char *zType; /* Data type for this column */ - CollSeq *pColl; /* Collating sequence. If NULL, use the default */ - u8 notNull; /* True if there is a NOT NULL constraint */ - u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ - char affinity; /* One of the SQLITE_AFF_... values */ -}; - -/* -** A "Collating Sequence" is defined by an instance of the following -** structure. Conceptually, a collating sequence consists of a name and -** a comparison routine that defines the order of that sequence. -** -** There may two seperate implementations of the collation function, one -** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that -** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine -** native byte order. When a collation sequence is invoked, SQLite selects -** the version that will require the least expensive encoding -** transalations, if any. -** -** The CollSeq.pUser member variable is an extra parameter that passed in -** as the first argument to the UTF-8 comparison function, xCmp. -** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function, -** xCmp16. -** -** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the -** collating sequence is undefined. Indices built on an undefined -** collating sequence may not be read or written. -*/ -struct CollSeq { - char *zName; /* Name of the collating sequence, UTF-8 encoded */ - u8 enc; /* Text encoding handled by xCmp() */ - void *pUser; /* First argument to xCmp() */ - int (*xCmp)(void*,int, const void*, int, const void*); -}; - -/* -** A sort order can be either ASC or DESC. -*/ -#define SQLITE_SO_ASC 0 /* Sort in ascending order */ -#define SQLITE_SO_DESC 1 /* Sort in ascending order */ - -/* -** Column affinity types. -*/ -#define SQLITE_AFF_INTEGER 'i' -#define SQLITE_AFF_NUMERIC 'n' -#define SQLITE_AFF_TEXT 't' -#define SQLITE_AFF_NONE 'o' - - -/* -** Each SQL table is represented in memory by an instance of the -** following structure. -** -** Table.zName is the name of the table. The case of the original -** CREATE TABLE statement is stored, but case is not significant for -** comparisons. -** -** Table.nCol is the number of columns in this table. Table.aCol is a -** pointer to an array of Column structures, one for each column. -** -** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of -** the column that is that key. Otherwise Table.iPKey is negative. Note -** that the datatype of the PRIMARY KEY must be INTEGER for this field to -** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of -** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid -** is generated for each row of the table. Table.hasPrimKey is true if -** the table has any PRIMARY KEY, INTEGER or otherwise. -** -** Table.tnum is the page number for the root BTree page of the table in the -** database file. If Table.iDb is the index of the database table backend -** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that -** holds temporary tables and indices. If Table.isTransient -** is true, then the table is stored in a file that is automatically deleted -** when the VDBE cursor to the table is closed. In this case Table.tnum -** refers VDBE cursor number that holds the table open, not to the root -** page number. Transient tables are used to hold the results of a -** sub-query that appears instead of a real table name in the FROM clause -** of a SELECT statement. -*/ -struct Table { - char *zName; /* Name of the table */ - int nCol; /* Number of columns in this table */ - Column *aCol; /* Information about each column */ - int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */ - Index *pIndex; /* List of SQL indexes on this table. */ - int tnum; /* Root BTree node for this table (see note above) */ - Select *pSelect; /* NULL for tables. Points to definition if a view. */ - u8 readOnly; /* True if this table should not be written by the user */ - u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */ - u8 isTransient; /* True if automatically deleted when VDBE finishes */ - u8 hasPrimKey; /* True if there exists a primary key */ - u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ - Trigger *pTrigger; /* List of SQL triggers on this table */ - FKey *pFKey; /* Linked list of all foreign keys in this table */ - char *zColAff; /* String defining the affinity of each column */ -}; - -/* -** Each foreign key constraint is an instance of the following structure. -** -** A foreign key is associated with two tables. The "from" table is -** the table that contains the REFERENCES clause that creates the foreign -** key. The "to" table is the table that is named in the REFERENCES clause. -** Consider this example: -** -** CREATE TABLE ex1( -** a INTEGER PRIMARY KEY, -** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) -** ); -** -** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". -** -** Each REFERENCES clause generates an instance of the following structure -** which is attached to the from-table. The to-table need not exist when -** the from-table is created. The existance of the to-table is not checked -** until an attempt is made to insert data into the from-table. -** -** The sqlite.aFKey hash table stores pointers to this structure -** given the name of a to-table. For each to-table, all foreign keys -** associated with that table are on a linked list using the FKey.pNextTo -** field. -*/ -struct FKey { - Table *pFrom; /* The table that constains the REFERENCES clause */ - FKey *pNextFrom; /* Next foreign key in pFrom */ - char *zTo; /* Name of table that the key points to */ - FKey *pNextTo; /* Next foreign key that points to zTo */ - int nCol; /* Number of columns in this key */ - struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ - int iFrom; /* Index of column in pFrom */ - char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ - } *aCol; /* One entry for each of nCol column s */ - u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ - u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ - u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ - u8 insertConf; /* How to resolve conflicts that occur on INSERT */ -}; - -/* -** SQLite supports many different ways to resolve a contraint -** error. ROLLBACK processing means that a constraint violation -** causes the operation in process to fail and for the current transaction -** to be rolled back. ABORT processing means the operation in process -** fails and any prior changes from that one operation are backed out, -** but the transaction is not rolled back. FAIL processing means that -** the operation in progress stops and returns an error code. But prior -** changes due to the same operation are not backed out and no rollback -** occurs. IGNORE means that the particular row that caused the constraint -** error is not inserted or updated. Processing continues and no error -** is returned. REPLACE means that preexisting database rows that caused -** a UNIQUE constraint violation are removed so that the new insert or -** update can proceed. Processing continues and no error is reported. -** -** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. -** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the -** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign -** key is set to NULL. CASCADE means that a DELETE or UPDATE of the -** referenced table row is propagated into the row that holds the -** foreign key. -** -** The following symbolic values are used to record which type -** of action to take. -*/ -#define OE_None 0 /* There is no constraint to check */ -#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ -#define OE_Abort 2 /* Back out changes but do no rollback transaction */ -#define OE_Fail 3 /* Stop the operation but leave all prior changes */ -#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ -#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ - -#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ -#define OE_SetNull 7 /* Set the foreign key value to NULL */ -#define OE_SetDflt 8 /* Set the foreign key value to its default */ -#define OE_Cascade 9 /* Cascade the changes */ - -#define OE_Default 99 /* Do whatever the default action is */ - - -/* -** An instance of the following structure is passed as the first -** argument to sqlite3VdbeKeyCompare and is used to control the -** comparison of the two index keys. -** -** If the KeyInfo.incrKey value is true and the comparison would -** otherwise be equal, then return a result as if the second key larger. -*/ -struct KeyInfo { - u8 enc; /* Text encoding - one of the TEXT_Utf* values */ - u8 incrKey; /* Increase 2nd key by epsilon before comparison */ - int nField; /* Number of entries in aColl[] */ - u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */ - CollSeq *aColl[1]; /* Collating sequence for each term of the key */ -}; - -/* -** Each SQL index is represented in memory by an -** instance of the following structure. -** -** The columns of the table that are to be indexed are described -** by the aiColumn[] field of this structure. For example, suppose -** we have the following table and index: -** -** CREATE TABLE Ex1(c1 int, c2 int, c3 text); -** CREATE INDEX Ex2 ON Ex1(c3,c1); -** -** In the Table structure describing Ex1, nCol==3 because there are -** three columns in the table. In the Index structure describing -** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. -** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the -** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. -** The second column to be indexed (c1) has an index of 0 in -** Ex1.aCol[], hence Ex2.aiColumn[1]==0. -** -** The Index.onError field determines whether or not the indexed columns -** must be unique and what to do if they are not. When Index.onError=OE_None, -** it means this is not a unique index. Otherwise it is a unique index -** and the value of Index.onError indicate the which conflict resolution -** algorithm to employ whenever an attempt is made to insert a non-unique -** element. -*/ -struct Index { - char *zName; /* Name of this index */ - int nColumn; /* Number of columns in the table used by this index */ - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - Table *pTable; /* The SQL table being indexed */ - int tnum; /* Page containing root of this index in database file */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ - u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */ - char *zColAff; /* String defining the affinity of each column */ - Index *pNext; /* The next index associated with the same table */ - KeyInfo keyInfo; /* Info on how to order keys. MUST BE LAST */ -}; - -/* -** Each token coming out of the lexer is an instance of -** this structure. Tokens are also used as part of an expression. -** -** Note if Token.z==0 then Token.dyn and Token.n are undefined and -** may contain random values. Do not make any assuptions about Token.dyn -** and Token.n when Token.z==0. -*/ -struct Token { - const unsigned char *z; /* Text of the token. Not NULL-terminated! */ - unsigned dyn : 1; /* True for malloced memory, false for static */ - unsigned n : 31; /* Number of characters in this token */ -}; - -/* -** Each node of an expression in the parse tree is an instance -** of this structure. -** -** Expr.op is the opcode. The integer parser token codes are reused -** as opcodes here. For example, the parser defines TK_GE to be an integer -** code representing the ">=" operator. This same integer code is reused -** to represent the greater-than-or-equal-to operator in the expression -** tree. -** -** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list -** of argument if the expression is a function. -** -** Expr.token is the operator token for this node. For some expressions -** that have subexpressions, Expr.token can be the complete text that gave -** rise to the Expr. In the latter case, the token is marked as being -** a compound token. -** -** An expression of the form ID or ID.ID refers to a column in a table. -** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is -** the integer cursor number of a VDBE cursor pointing to that table and -** Expr.iColumn is the column number for the specific column. If the -** expression is used as a result in an aggregate SELECT, then the -** value is also stored in the Expr.iAgg column in the aggregate so that -** it can be accessed after all aggregates are computed. -** -** If the expression is a function, the Expr.iTable is an integer code -** representing which function. If the expression is an unbound variable -** marker (a question mark character '?' in the original SQL) then the -** Expr.iTable holds the index number for that variable. -** -** The Expr.pSelect field points to a SELECT statement. The SELECT might -** be the right operand of an IN operator. Or, if a scalar SELECT appears -** in an expression the opcode is TK_SELECT and Expr.pSelect is the only -** operand. -*/ -struct Expr { - u8 op; /* Operation performed by this node */ - char affinity; /* The affinity of the column or 0 if not a column */ - u8 iDb; /* Database referenced by this expression */ - u8 flags; /* Various flags. See below */ - CollSeq *pColl; /* The collation type of the column or 0 */ - Expr *pLeft, *pRight; /* Left and right subnodes */ - ExprList *pList; /* A list of expressions used as function arguments - ** or in "<expr> IN (<expr-list)" */ - Token token; /* An operand token */ - Token span; /* Complete text of the expression */ - int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the - ** iColumn-th field of the iTable-th table. */ - int iAgg; /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull - ** result from the iAgg-th element of the aggregator */ - Select *pSelect; /* When the expression is a sub-select. Also the - ** right side of "<expr> IN (<select>)" */ -}; - -/* -** The following are the meanings of bits in the Expr.flags field. -*/ -#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */ - -/* -** These macros can be used to test, set, or clear bits in the -** Expr.flags field. -*/ -#define ExprHasProperty(E,P) (((E)->flags&(P))==(P)) -#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0) -#define ExprSetProperty(E,P) (E)->flags|=(P) -#define ExprClearProperty(E,P) (E)->flags&=~(P) - -/* -** A list of expressions. Each expression may optionally have a -** name. An expr/name combination can be used in several ways, such -** as the list of "expr AS ID" fields following a "SELECT" or in the -** list of "ID = expr" items in an UPDATE. A list of expressions can -** also be used as the argument to a function, in which case the a.zName -** field is not used. -*/ -struct ExprList { - int nExpr; /* Number of expressions on the list */ - int nAlloc; /* Number of entries allocated below */ - struct ExprList_item { - Expr *pExpr; /* The list of expressions */ - char *zName; /* Token associated with this expression */ - u8 sortOrder; /* 1 for DESC or 0 for ASC */ - u8 isAgg; /* True if this is an aggregate like count(*) */ - u8 done; /* A flag to indicate when processing is finished */ - } *a; /* One entry for each expression */ -}; - -/* -** An instance of this structure can hold a simple list of identifiers, -** such as the list "a,b,c" in the following statements: -** -** INSERT INTO t(a,b,c) VALUES ...; -** CREATE INDEX idx ON t(a,b,c); -** CREATE TRIGGER trig BEFORE UPDATE ON t(a,b,c) ...; -** -** The IdList.a.idx field is used when the IdList represents the list of -** column names after a table name in an INSERT statement. In the statement -** -** INSERT INTO t(a,b,c) ... -** -** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. -*/ -struct IdList { - int nId; /* Number of identifiers on the list */ - int nAlloc; /* Number of entries allocated for a[] below */ - struct IdList_item { - char *zName; /* Name of the identifier */ - int idx; /* Index in some Table.aCol[] of a column named zName */ - } *a; -}; - -/* -** The following structure describes the FROM clause of a SELECT statement. -** Each table or subquery in the FROM clause is a separate element of -** the SrcList.a[] array. -** -** With the addition of multiple database support, the following structure -** can also be used to describe a particular table such as the table that -** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, -** such a table must be a simple name: ID. But in SQLite, the table can -** now be identified by a database name, a dot, then the table name: ID.ID. -*/ -struct SrcList { - i16 nSrc; /* Number of tables or subqueries in the FROM clause */ - i16 nAlloc; /* Number of entries allocated in a[] below */ - struct SrcList_item { - char *zDatabase; /* Name of database holding this table */ - char *zName; /* Name of the table */ - char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ - Table *pTab; /* An SQL table corresponding to zName */ - Select *pSelect; /* A SELECT statement used in place of a table name */ - int jointype; /* Type of join between this table and the next */ - int iCursor; /* The VDBE cursor number used to access this table */ - Expr *pOn; /* The ON clause of a join */ - IdList *pUsing; /* The USING clause of a join */ - } a[1]; /* One entry for each identifier on the list */ -}; - -/* -** Permitted values of the SrcList.a.jointype field -*/ -#define JT_INNER 0x0001 /* Any kind of inner or cross join */ -#define JT_NATURAL 0x0002 /* True for a "natural" join */ -#define JT_LEFT 0x0004 /* Left outer join */ -#define JT_RIGHT 0x0008 /* Right outer join */ -#define JT_OUTER 0x0010 /* The "OUTER" keyword is present */ -#define JT_ERROR 0x0020 /* unknown or unsupported join type */ - -/* -** For each nested loop in a WHERE clause implementation, the WhereInfo -** structure contains a single instance of this structure. This structure -** is intended to be private the the where.c module and should not be -** access or modified by other modules. -*/ -struct WhereLevel { - int iMem; /* Memory cell used by this level */ - Index *pIdx; /* Index used */ - int iCur; /* Cursor number used for this index */ - int score; /* How well this indexed scored */ - int brk; /* Jump here to break out of the loop */ - int cont; /* Jump here to continue with the next loop cycle */ - int op, p1, p2; /* Opcode used to terminate the loop */ - int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */ - int top; /* First instruction of interior of the loop */ - int inOp, inP1, inP2;/* Opcode used to implement an IN operator */ - int bRev; /* Do the scan in the reverse direction */ -}; - -/* -** The WHERE clause processing routine has two halves. The -** first part does the start of the WHERE loop and the second -** half does the tail of the WHERE loop. An instance of -** this structure is returned by the first half and passed -** into the second half to give some continuity. -*/ -struct WhereInfo { - Parse *pParse; - SrcList *pTabList; /* List of tables in the join */ - int iContinue; /* Jump here to continue with next record */ - int iBreak; /* Jump here to break out of the loop */ - int nLevel; /* Number of nested loop */ - WhereLevel a[1]; /* Information about each nest loop in the WHERE */ -}; - -/* -** An instance of the following structure contains all information -** needed to generate code for a single SELECT statement. -** -** The zSelect field is used when the Select structure must be persistent. -** Normally, the expression tree points to tokens in the original input -** string that encodes the select. But if the Select structure must live -** longer than its input string (for example when it is used to describe -** a VIEW) we have to make a copy of the input string so that the nodes -** of the expression tree will have something to point to. zSelect is used -** to hold that copy. -** -** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0. -** If there is a LIMIT clause, the parser sets nLimit to the value of the -** limit and nOffset to the value of the offset (or 0 if there is not -** offset). But later on, nLimit and nOffset become the memory locations -** in the VDBE that record the limit and offset counters. -*/ -struct Select { - ExprList *pEList; /* The fields of the result */ - u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ - u8 isDistinct; /* True if the DISTINCT keyword is present */ - SrcList *pSrc; /* The FROM clause */ - Expr *pWhere; /* The WHERE clause */ - ExprList *pGroupBy; /* The GROUP BY clause */ - Expr *pHaving; /* The HAVING clause */ - ExprList *pOrderBy; /* The ORDER BY clause */ - Select *pPrior; /* Prior select in a compound select statement */ - int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */ - int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ - char *zSelect; /* Complete text of the SELECT command */ - IdList **ppOpenTemp; /* OP_OpenTemp addresses used by multi-selects */ -}; - -/* -** The results of a select can be distributed in several ways. -*/ -#define SRT_Callback 1 /* Invoke a callback with each row of result */ -#define SRT_Mem 2 /* Store result in a memory cell */ -#define SRT_Set 3 /* Store result as unique keys in a table */ -#define SRT_Union 5 /* Store result as keys in a table */ -#define SRT_Except 6 /* Remove result from a UNION table */ -#define SRT_Table 7 /* Store result as data with a unique key */ -#define SRT_TempTable 8 /* Store result in a trasient table */ -#define SRT_Discard 9 /* Do not save the results anywhere */ -#define SRT_Sorter 10 /* Store results in the sorter */ -#define SRT_Subroutine 11 /* Call a subroutine to handle results */ - -/* -** When a SELECT uses aggregate functions (like "count(*)" or "avg(f1)") -** we have to do some additional analysis of expressions. An instance -** of the following structure holds information about a single subexpression -** somewhere in the SELECT statement. An array of these structures holds -** all the information we need to generate code for aggregate -** expressions. -** -** Note that when analyzing a SELECT containing aggregates, both -** non-aggregate field variables and aggregate functions are stored -** in the AggExpr array of the Parser structure. -** -** The pExpr field points to an expression that is part of either the -** field list, the GROUP BY clause, the HAVING clause or the ORDER BY -** clause. The expression will be freed when those clauses are cleaned -** up. Do not try to delete the expression attached to AggExpr.pExpr. -** -** If AggExpr.pExpr==0, that means the expression is "count(*)". -*/ -struct AggExpr { - int isAgg; /* if TRUE contains an aggregate function */ - Expr *pExpr; /* The expression */ - FuncDef *pFunc; /* Information about the aggregate function */ -}; - -/* -** An SQL parser context. A copy of this structure is passed through -** the parser and down into all the parser action routine in order to -** carry around information that is global to the entire parse. -*/ -struct Parse { - sqlite3 *db; /* The main database structure */ - int rc; /* Return code from execution */ - char *zErrMsg; /* An error message */ - Token sErrToken; /* The token at which the error occurred */ - Token sNameToken; /* Token with unqualified schema object name */ - Token sLastToken; /* The last token parsed */ - const char *zSql; /* All SQL text */ - const char *zTail; /* All SQL text past the last semicolon parsed */ - Table *pNewTable; /* A table being constructed by CREATE TABLE */ - Vdbe *pVdbe; /* An engine for executing database bytecode */ - u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ - u8 explain; /* True if the EXPLAIN flag is found on the query */ - u8 nameClash; /* A permanent table name clashes with temp table name */ - u8 useAgg; /* If true, extract field values from the aggregator - ** while generating expressions. Normally false */ - u8 checkSchema; /* Causes schema cookie check after an error */ - int nErr; /* Number of errors seen */ - int nTab; /* Number of previously allocated VDBE cursors */ - int nMem; /* Number of memory cells used so far */ - int nSet; /* Number of sets used so far */ - int nAgg; /* Number of aggregate expressions */ - int nVar; /* Number of '?' variables seen in the SQL so far */ - int nVarExpr; /* Number of used slots in apVarExpr[] */ - int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ - Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ - AggExpr *aAgg; /* An array of aggregate expressions */ - const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ - Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ - TriggerStack *trigStack; /* Trigger actions being coded */ - u32 cookieMask; /* Bitmask of schema verified databases */ - int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */ - int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ - u32 writeMask; /* Start a write transaction on these databases */ -}; - -/* -** An instance of the following structure can be declared on a stack and used -** to save the Parse.zAuthContext value so that it can be restored later. -*/ -struct AuthContext { - const char *zAuthContext; /* Put saved Parse.zAuthContext here */ - Parse *pParse; /* The Parse structure */ -}; - -/* -** Bitfield flags for P2 value in OP_PutIntKey and OP_Delete -*/ -#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */ -#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */ - -/* - * Each trigger present in the database schema is stored as an instance of - * struct Trigger. - * - * Pointers to instances of struct Trigger are stored in two ways. - * 1. In the "trigHash" hash table (part of the sqlite3* that represents the - * database). This allows Trigger structures to be retrieved by name. - * 2. All triggers associated with a single table form a linked list, using the - * pNext member of struct Trigger. A pointer to the first element of the - * linked list is stored as the "pTrigger" member of the associated - * struct Table. - * - * The "step_list" member points to the first element of a linked list - * containing the SQL statements specified as the trigger program. - */ -struct Trigger { - char *name; /* The name of the trigger */ - char *table; /* The table or view to which the trigger applies */ - u8 iDb; /* Database containing this trigger */ - u8 iTabDb; /* Database containing Trigger.table */ - u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ - u8 tr_tm; /* One of TK_BEFORE, TK_AFTER */ - Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */ - IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger, - the <column-list> is stored here */ - int foreach; /* One of TK_ROW or TK_STATEMENT */ - Token nameToken; /* Token containing zName. Use during parsing only */ - - TriggerStep *step_list; /* Link list of trigger program steps */ - Trigger *pNext; /* Next trigger associated with the table */ -}; - -/* - * An instance of struct TriggerStep is used to store a single SQL statement - * that is a part of a trigger-program. - * - * Instances of struct TriggerStep are stored in a singly linked list (linked - * using the "pNext" member) referenced by the "step_list" member of the - * associated struct Trigger instance. The first element of the linked list is - * the first step of the trigger-program. - * - * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or - * "SELECT" statement. The meanings of the other members is determined by the - * value of "op" as follows: - * - * (op == TK_INSERT) - * orconf -> stores the ON CONFLICT algorithm - * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then - * this stores a pointer to the SELECT statement. Otherwise NULL. - * target -> A token holding the name of the table to insert into. - * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then - * this stores values to be inserted. Otherwise NULL. - * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ... - * statement, then this stores the column-names to be - * inserted into. - * - * (op == TK_DELETE) - * target -> A token holding the name of the table to delete from. - * pWhere -> The WHERE clause of the DELETE statement if one is specified. - * Otherwise NULL. - * - * (op == TK_UPDATE) - * target -> A token holding the name of the table to update rows of. - * pWhere -> The WHERE clause of the UPDATE statement if one is specified. - * Otherwise NULL. - * pExprList -> A list of the columns to update and the expressions to update - * them to. See sqlite3Update() documentation of "pChanges" - * argument. - * - */ -struct TriggerStep { - int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ - int orconf; /* OE_Rollback etc. */ - Trigger *pTrig; /* The trigger that this step is a part of */ - - Select *pSelect; /* Valid for SELECT and sometimes - INSERT steps (when pExprList == 0) */ - Token target; /* Valid for DELETE, UPDATE, INSERT steps */ - Expr *pWhere; /* Valid for DELETE, UPDATE steps */ - ExprList *pExprList; /* Valid for UPDATE statements and sometimes - INSERT steps (when pSelect == 0) */ - IdList *pIdList; /* Valid for INSERT statements only */ - - TriggerStep * pNext; /* Next in the link-list */ -}; - -/* - * An instance of struct TriggerStack stores information required during code - * generation of a single trigger program. While the trigger program is being - * coded, its associated TriggerStack instance is pointed to by the - * "pTriggerStack" member of the Parse structure. - * - * The pTab member points to the table that triggers are being coded on. The - * newIdx member contains the index of the vdbe cursor that points at the temp - * table that stores the new.* references. If new.* references are not valid - * for the trigger being coded (for example an ON DELETE trigger), then newIdx - * is set to -1. The oldIdx member is analogous to newIdx, for old.* references. - * - * The ON CONFLICT policy to be used for the trigger program steps is stored - * as the orconf member. If this is OE_Default, then the ON CONFLICT clause - * specified for individual triggers steps is used. - * - * struct TriggerStack has a "pNext" member, to allow linked lists to be - * constructed. When coding nested triggers (triggers fired by other triggers) - * each nested trigger stores its parent trigger's TriggerStack as the "pNext" - * pointer. Once the nested trigger has been coded, the pNext value is restored - * to the pTriggerStack member of the Parse stucture and coding of the parent - * trigger continues. - * - * Before a nested trigger is coded, the linked list pointed to by the - * pTriggerStack is scanned to ensure that the trigger is not about to be coded - * recursively. If this condition is detected, the nested trigger is not coded. - */ -struct TriggerStack { - Table *pTab; /* Table that triggers are currently being coded on */ - int newIdx; /* Index of vdbe cursor to "new" temp table */ - int oldIdx; /* Index of vdbe cursor to "old" temp table */ - int orconf; /* Current orconf policy */ - int ignoreJump; /* where to jump to for a RAISE(IGNORE) */ - Trigger *pTrigger; /* The trigger currently being coded */ - TriggerStack *pNext; /* Next trigger down on the trigger stack */ -}; - -/* -** The following structure contains information used by the sqliteFix... -** routines as they walk the parse tree to make database references -** explicit. -*/ -typedef struct DbFixer DbFixer; -struct DbFixer { - Parse *pParse; /* The parsing context. Error messages written here */ - const char *zDb; /* Make sure all objects are contained in this database */ - const char *zType; /* Type of the container - used for error messages */ - const Token *pName; /* Name of the container - used for error messages */ -}; - -/* -** A pointer to this structure is used to communicate information -** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback. -*/ -typedef struct { - sqlite3 *db; /* The database being initialized */ - char **pzErrMsg; /* Error message stored here */ -} InitData; - - -/* - * This global flag is set for performance testing of triggers. When it is set - * SQLite will perform the overhead of building new and old trigger references - * even when no triggers exist - */ -extern int sqlite3_always_code_trigger_setup; - -/* -** Internal function prototypes -*/ -int sqlite3StrICmp(const char *, const char *); -int sqlite3StrNICmp(const char *, const char *, int); -int sqlite3HashNoCase(const char *, int); -int sqlite3IsNumber(const char*, int*, u8); -int sqlite3Compare(const char *, const char *); -int sqlite3SortCompare(const char *, const char *); -void sqlite3RealToSortable(double r, char *); -#ifdef SQLITE_DEBUG - void *sqlite3Malloc_(int,int,char*,int); - void sqlite3Free_(void*,char*,int); - void *sqlite3Realloc_(void*,int,char*,int); - char *sqlite3StrDup_(const char*,char*,int); - char *sqlite3StrNDup_(const char*, int,char*,int); - void sqlite3CheckMemory(void*,int); -#else - void *sqlite3Malloc(int); - void *sqlite3MallocRaw(int); - void sqlite3Free(void*); - void *sqlite3Realloc(void*,int); - char *sqlite3StrDup(const char*); - char *sqlite3StrNDup(const char*, int); -# define sqlite3CheckMemory(a,b) -#endif -void sqlite3FreeX(void*); -char *sqlite3MPrintf(const char*, ...); -char *sqlite3VMPrintf(const char*, va_list); -void sqlite3DebugPrintf(const char*, ...); -void *sqlite3TextToPtr(const char*); -void sqlite3SetString(char **, const char *, ...); -void sqlite3ErrorMsg(Parse*, const char*, ...); -void sqlite3Dequote(char*); -int sqlite3KeywordCode(const char*, int); -int sqlite3RunParser(Parse*, const char*, char **); -void sqlite3FinishCoding(Parse*); -Expr *sqlite3Expr(int, Expr*, Expr*, Token*); -Expr *sqlite3ExprAnd(Expr*, Expr*); -void sqlite3ExprSpan(Expr*,Token*,Token*); -Expr *sqlite3ExprFunction(ExprList*, Token*); -void sqlite3ExprAssignVarNumber(Parse*, Expr*); -void sqlite3ExprDelete(Expr*); -ExprList *sqlite3ExprListAppend(ExprList*,Expr*,Token*); -void sqlite3ExprListDelete(ExprList*); -int sqlite3Init(sqlite3*, char**); -int sqlite3InitCallback(void*, int, char**, char**); -void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); -void sqlite3ResetInternalSchema(sqlite3*, int); -void sqlite3BeginParse(Parse*,int); -void sqlite3RollbackInternalChanges(sqlite3*); -void sqlite3CommitInternalChanges(sqlite3*); -Table *sqlite3ResultSetOfSelect(Parse*,char*,Select*); -void sqlite3OpenMasterTable(Vdbe *v, int); -void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int); -void sqlite3AddColumn(Parse*,Token*); -void sqlite3AddNotNull(Parse*, int); -void sqlite3AddPrimaryKey(Parse*, ExprList*, int); -void sqlite3AddColumnType(Parse*,Token*,Token*); -void sqlite3AddDefaultValue(Parse*,Token*,int); -void sqlite3AddCollateType(Parse*, const char*, int); -void sqlite3EndTable(Parse*,Token*,Select*); -void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int); -int sqlite3ViewGetColumnNames(Parse*,Table*); -void sqlite3DropTable(Parse*, SrcList*, int); -void sqlite3DeleteTable(sqlite3*, Table*); -void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); -IdList *sqlite3IdListAppend(IdList*, Token*); -int sqlite3IdListIndex(IdList*,const char*); -SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*); -void sqlite3SrcListAddAlias(SrcList*, Token*); -void sqlite3SrcListAssignCursors(Parse*, SrcList*); -void sqlite3IdListDelete(IdList*); -void sqlite3SrcListDelete(SrcList*); -void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, - Token*); -void sqlite3DropIndex(Parse*, SrcList*); -void sqlite3AddKeyType(Vdbe*, ExprList*); -void sqlite3AddIdxKeyType(Vdbe*, Index*); -int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff); -Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*, - int,int,int); -void sqlite3SelectDelete(Select*); -void sqlite3SelectUnbind(Select*); -Table *sqlite3SrcListLookup(Parse*, SrcList*); -int sqlite3IsReadOnly(Parse*, Table*, int); -void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*); -void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); -void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**); -void sqlite3WhereEnd(WhereInfo*); -void sqlite3ExprCode(Parse*, Expr*); -int sqlite3ExprCodeExprList(Parse*, ExprList*); -void sqlite3ExprIfTrue(Parse*, Expr*, int, int); -void sqlite3ExprIfFalse(Parse*, Expr*, int, int); -Table *sqlite3FindTable(sqlite3*,const char*, const char*); -Table *sqlite3LocateTable(Parse*,const char*, const char*); -Index *sqlite3FindIndex(sqlite3*,const char*, const char*); -void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); -void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); -void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); -void sqlite3Vacuum(Parse*, Token*); -int sqlite3RunVacuum(char**, sqlite3*); -char *sqlite3NameFromToken(Token*); -int sqlite3ExprCheck(Parse*, Expr*, int, int*); -int sqlite3ExprCompare(Expr*, Expr*); -int sqliteFuncId(Token*); -int sqlite3ExprResolveIds(Parse*, SrcList*, ExprList*, Expr*); -int sqlite3ExprResolveAndCheck(Parse*,SrcList*,ExprList*,Expr*,int,int*); -int sqlite3ExprAnalyzeAggregates(Parse*, Expr*); -Vdbe *sqlite3GetVdbe(Parse*); -void sqlite3Randomness(int, void*); -void sqlite3RollbackAll(sqlite3*); -void sqlite3CodeVerifySchema(Parse*, int); -void sqlite3BeginTransaction(Parse*, int); -void sqlite3CommitTransaction(Parse*); -void sqlite3RollbackTransaction(Parse*); -int sqlite3ExprIsConstant(Expr*); -int sqlite3ExprIsInteger(Expr*, int*); -int sqlite3IsRowid(const char*); -void sqlite3GenerateRowDelete(sqlite3*, Vdbe*, Table*, int, int); -void sqlite3GenerateRowIndexDelete(sqlite3*, Vdbe*, Table*, int, char*); -void sqlite3GenerateIndexKey(Vdbe*, Index*, int); -void sqlite3GenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); -void sqlite3CompleteInsertion(Parse*, Table*, int, char*, int, int, int); -void sqlite3OpenTableAndIndices(Parse*, Table*, int, int); -void sqlite3BeginWriteOperation(Parse*, int, int); -Expr *sqlite3ExprDup(Expr*); -void sqlite3TokenCopy(Token*, Token*); -ExprList *sqlite3ExprListDup(ExprList*); -SrcList *sqlite3SrcListDup(SrcList*); -IdList *sqlite3IdListDup(IdList*); -Select *sqlite3SelectDup(Select*); -FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int); -void sqlite3RegisterBuiltinFunctions(sqlite3*); -void sqlite3RegisterDateTimeFunctions(sqlite3*); -int sqlite3SafetyOn(sqlite3*); -int sqlite3SafetyOff(sqlite3*); -int sqlite3SafetyCheck(sqlite3*); -void sqlite3ChangeCookie(sqlite3*, Vdbe*, int); -void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, - int,Expr*,int); -void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); -void sqlite3DropTrigger(Parse*, SrcList*); -void sqlite3DropTriggerPtr(Parse*, Trigger*, int); -int sqlite3TriggersExist(Parse* , Trigger* , int , int , int, ExprList*); -int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, - int, int); -void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); -void sqlite3DeleteTriggerStep(TriggerStep*); -TriggerStep *sqlite3TriggerSelectStep(Select*); -TriggerStep *sqlite3TriggerInsertStep(Token*, IdList*, ExprList*, Select*, int); -TriggerStep *sqlite3TriggerUpdateStep(Token*, ExprList*, Expr*, int); -TriggerStep *sqlite3TriggerDeleteStep(Token*, Expr*); -void sqlite3DeleteTrigger(Trigger*); -int sqlite3JoinType(Parse*, Token*, Token*, Token*); -void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); -void sqlite3DeferForeignKey(Parse*, int); -#ifndef SQLITE_OMIT_AUTHORIZATION - void sqlite3AuthRead(Parse*,Expr*,SrcList*); - int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*); - void sqlite3AuthContextPush(Parse*, AuthContext*, const char*); - void sqlite3AuthContextPop(AuthContext*); -#else -# define sqlite3AuthRead(a,b,c) -# define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK -# define sqlite3AuthContextPush(a,b,c) -# define sqlite3AuthContextPop(a) ((void)(a)) -#endif -void sqlite3Attach(Parse*, Token*, Token*, int, Token*); -void sqlite3Detach(Parse*, Token*); -int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename, - int omitJournal, int nCache, Btree **ppBtree); -int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); -int sqlite3FixSrcList(DbFixer*, SrcList*); -int sqlite3FixSelect(DbFixer*, Select*); -int sqlite3FixExpr(DbFixer*, Expr*); -int sqlite3FixExprList(DbFixer*, ExprList*); -int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); -double sqlite3AtoF(const char *z, const char **); -char *sqlite3_snprintf(int,char*,const char*,...); -int sqlite3GetInt32(const char *, int*); -int sqlite3FitsIn64Bits(const char *); -int sqlite3utf16ByteLen(const void *pData, int nChar); -int sqlite3utf8CharLen(const char *pData, int nByte); -int sqlite3ReadUtf8(const unsigned char *); -int sqlite3PutVarint(unsigned char *, u64); -int sqlite3GetVarint(const unsigned char *, u64 *); -int sqlite3GetVarint32(const unsigned char *, u32 *); -int sqlite3VarintLen(u64 v); -char sqlite3AffinityType(const char *, int); -void sqlite3IndexAffinityStr(Vdbe *, Index *); -void sqlite3TableAffinityStr(Vdbe *, Table *); -char sqlite3CompareAffinity(Expr *pExpr, char aff2); -int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); -char sqlite3ExprAffinity(Expr *pExpr); -int sqlite3atoi64(const char*, i64*); -void sqlite3Error(sqlite3*, int, const char*,...); -void *sqlite3HexToBlob(const char *z); -int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); -const char *sqlite3ErrStr(int); -int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold); -int sqlite3ReadSchema(Parse *pParse); -CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char *,int,int); -CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName); -CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); -int sqlite3CheckCollSeq(Parse *, CollSeq *); -int sqlite3CheckIndexCollSeq(Parse *, Index *); -int sqlite3CheckObjectName(Parse *, const char *); -void sqlite3VdbeSetChanges(sqlite3 *, int); -void sqlite3utf16Substr(sqlite3_context *,int,sqlite3_value **); - -const void *sqlite3ValueText(sqlite3_value*, u8); -int sqlite3ValueBytes(sqlite3_value*, u8); -void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); -void sqlite3ValueFree(sqlite3_value*); -sqlite3_value *sqlite3ValueNew(); -sqlite3_value *sqlite3GetTransientValue(sqlite3*db); -extern const unsigned char sqlite3UpperToLower[]; - -#endif diff --git a/kopete/plugins/statistics/sqlite/table.c b/kopete/plugins/statistics/sqlite/table.c deleted file mode 100644 index d4ef2c8a..00000000 --- a/kopete/plugins/statistics/sqlite/table.c +++ /dev/null @@ -1,195 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the sqlite3_get_table() and sqlite3_free_table() -** interface routines. These are just wrappers around the main -** interface routine of sqlite3_exec(). -** -** These routines are in a separate files so that they will not be linked -** if they are not used. -*/ -#include <stdlib.h> -#include <string.h> -#include "sqliteInt.h" - -/* -** This structure is used to pass data from sqlite3_get_table() through -** to the callback function is uses to build the result. -*/ -typedef struct TabResult { - char **azResult; - char *zErrMsg; - int nResult; - int nAlloc; - int nRow; - int nColumn; - int nData; - int rc; -} TabResult; - -/* -** This routine is called once for each row in the result table. Its job -** is to fill in the TabResult structure appropriately, allocating new -** memory as necessary. -*/ -static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ - TabResult *p = (TabResult*)pArg; - int need; - int i; - char *z; - - /* Make sure there is enough space in p->azResult to hold everything - ** we need to remember from this invocation of the callback. - */ - if( p->nRow==0 && argv!=0 ){ - need = nCol*2; - }else{ - need = nCol; - } - if( p->nData + need >= p->nAlloc ){ - char **azNew; - p->nAlloc = p->nAlloc*2 + need + 1; - azNew = realloc( p->azResult, sizeof(char*)*p->nAlloc ); - if( azNew==0 ) goto malloc_failed; - p->azResult = azNew; - } - - /* If this is the first row, then generate an extra row containing - ** the names of all columns. - */ - if( p->nRow==0 ){ - p->nColumn = nCol; - for(i=0; i<nCol; i++){ - if( colv[i]==0 ){ - z = 0; - }else{ - z = malloc( strlen(colv[i])+1 ); - if( z==0 ) goto malloc_failed; - strcpy(z, colv[i]); - } - p->azResult[p->nData++] = z; - } - }else if( p->nColumn!=nCol ){ - sqlite3SetString(&p->zErrMsg, - "sqlite3_get_table() called with two or more incompatible queries", - (char*)0); - p->rc = SQLITE_ERROR; - return 1; - } - - /* Copy over the row data - */ - if( argv!=0 ){ - for(i=0; i<nCol; i++){ - if( argv[i]==0 ){ - z = 0; - }else{ - z = malloc( strlen(argv[i])+1 ); - if( z==0 ) goto malloc_failed; - strcpy(z, argv[i]); - } - p->azResult[p->nData++] = z; - } - p->nRow++; - } - return 0; - -malloc_failed: - p->rc = SQLITE_NOMEM; - return 1; -} - -/* -** Query the database. But instead of invoking a callback for each row, -** malloc() for space to hold the result and return the entire results -** at the conclusion of the call. -** -** The result that is written to ***pazResult is held in memory obtained -** from malloc(). But the caller cannot free this memory directly. -** Instead, the entire table should be passed to sqlite3_free_table() when -** the calling procedure is finished using it. -*/ -int sqlite3_get_table( - sqlite3 *db, /* The database on which the SQL executes */ - const char *zSql, /* The SQL to be executed */ - char ***pazResult, /* Write the result table here */ - int *pnRow, /* Write the number of rows in the result here */ - int *pnColumn, /* Write the number of columns of result here */ - char **pzErrMsg /* Write error messages here */ -){ - int rc; - TabResult res; - if( pazResult==0 ){ return SQLITE_ERROR; } - *pazResult = 0; - if( pnColumn ) *pnColumn = 0; - if( pnRow ) *pnRow = 0; - res.zErrMsg = 0; - res.nResult = 0; - res.nRow = 0; - res.nColumn = 0; - res.nData = 1; - res.nAlloc = 20; - res.rc = SQLITE_OK; - res.azResult = malloc( sizeof(char*)*res.nAlloc ); - if( res.azResult==0 ) return SQLITE_NOMEM; - res.azResult[0] = 0; - rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg); - if( res.azResult ){ - res.azResult[0] = (char*)res.nData; - } - if( rc==SQLITE_ABORT ){ - sqlite3_free_table(&res.azResult[1]); - if( res.zErrMsg ){ - if( pzErrMsg ){ - free(*pzErrMsg); - *pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg); - } - sqliteFree(res.zErrMsg); - } - db->errCode = res.rc; - return res.rc; - } - sqliteFree(res.zErrMsg); - if( rc!=SQLITE_OK ){ - sqlite3_free_table(&res.azResult[1]); - return rc; - } - if( res.nAlloc>res.nData ){ - char **azNew; - azNew = realloc( res.azResult, sizeof(char*)*(res.nData+1) ); - if( azNew==0 ){ - sqlite3_free_table(&res.azResult[1]); - return SQLITE_NOMEM; - } - res.nAlloc = res.nData+1; - res.azResult = azNew; - } - *pazResult = &res.azResult[1]; - if( pnColumn ) *pnColumn = res.nColumn; - if( pnRow ) *pnRow = res.nRow; - return rc; -} - -/* -** This routine frees the space the sqlite3_get_table() malloced. -*/ -void sqlite3_free_table( - char **azResult /* Result returned from from sqlite3_get_table() */ -){ - if( azResult ){ - int i, n; - azResult--; - if( azResult==0 ) return; - n = (int)azResult[0]; - for(i=1; i<n; i++){ if( azResult[i] ) free(azResult[i]); } - free(azResult); - } -} diff --git a/kopete/plugins/statistics/sqlite/tokenize.c b/kopete/plugins/statistics/sqlite/tokenize.c deleted file mode 100644 index 061e5b9a..00000000 --- a/kopete/plugins/statistics/sqlite/tokenize.c +++ /dev/null @@ -1,707 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** An tokenizer for SQL -** -** This file contains C code that splits an SQL input string up into -** individual tokens and sends those tokens one-by-one over to the -** parser for analysis. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> -#include <stdlib.h> - -/* -** This function looks up an identifier to determine if it is a -** keyword. If it is a keyword, the token code of that keyword is -** returned. If the input is not a keyword, TK_ID is returned. -** -** The implementation of this routine was generated by a program, -** mkkeywordhash.c, located in the tool subdirectory of the distribution. -** The output of the mkkeywordhash.c program was manually cut and pasted -** into this file. When the set of keywords for SQLite changes, you -** must modify the mkkeywordhash.c program (to add or remove keywords from -** the data tables) then rerun that program to regenerate this function. -*/ -int sqlite3KeywordCode(const char *z, int n){ - static const char zText[519] = - "ABORTAFTERALLANDASCATTACHBEFOREBEGINBETWEENBYCASCADECASECHECK" - "COLLATECOMMITCONFLICTCONSTRAINTCREATECROSSDATABASEDEFAULTDEFERRABLE" - "DEFERREDDELETEDESCDETACHDISTINCTDROPEACHELSEENDEXCEPTEXCLUSIVE" - "EXPLAINFAILFOREIGNFROMFULLGLOBGROUPHAVINGIGNOREIMMEDIATEINDEX" - "INITIALLYINNERINSERTINSTEADINTERSECTINTOISNULLJOINKEYLEFTLIKE" - "LIMITMATCHNATURALNOTNULLNULLOFFSETONORDEROUTERPRAGMAPRIMARYRAISE" - "REFERENCESREPLACERESTRICTRIGHTROLLBACKROWSELECTSETSTATEMENTTABLE" - "TEMPORARYTHENTRANSACTIONTRIGGERUNIONUNIQUEUPDATEUSINGVACUUMVALUES" - "VIEWWHENWHERE"; - static const unsigned char aHash[154] = { - 0, 75, 82, 0, 0, 97, 80, 0, 83, 0, 0, 0, 0, - 0, 0, 6, 0, 95, 4, 0, 0, 0, 0, 0, 0, 0, - 0, 96, 86, 8, 0, 26, 13, 7, 19, 15, 0, 0, 32, - 25, 0, 21, 31, 41, 0, 0, 0, 34, 27, 0, 0, 30, - 0, 0, 0, 9, 0, 10, 0, 0, 0, 0, 51, 0, 44, - 43, 0, 45, 40, 0, 29, 39, 35, 0, 0, 20, 0, 59, - 0, 16, 0, 17, 0, 18, 0, 55, 42, 72, 0, 33, 0, - 0, 61, 66, 56, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 0, 0, 0, 74, 50, 76, 64, 52, 0, 0, 0, 0, - 68, 84, 0, 47, 0, 58, 60, 92, 0, 0, 48, 0, 93, - 0, 63, 71, 98, 0, 0, 0, 0, 0, 67, 0, 0, 0, - 0, 87, 0, 0, 0, 0, 0, 90, 88, 0, 94, - }; - static const unsigned char aNext[98] = { - 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 12, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, - 0, 0, 0, 14, 3, 24, 0, 0, 0, 1, 22, 0, 0, - 36, 23, 28, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, - 0, 49, 37, 0, 0, 0, 38, 0, 53, 0, 57, 62, 0, - 0, 0, 0, 0, 0, 70, 46, 0, 65, 0, 0, 0, 0, - 69, 73, 0, 77, 0, 0, 0, 0, 0, 0, 81, 85, 0, - 91, 79, 78, 0, 0, 89, 0, - }; - static const unsigned char aLen[98] = { - 5, 5, 3, 3, 2, 3, 6, 6, 5, 7, 2, 7, 4, - 5, 7, 6, 8, 10, 6, 5, 8, 7, 10, 8, 6, 4, - 6, 8, 4, 4, 4, 3, 6, 9, 7, 4, 3, 7, 4, - 4, 4, 5, 6, 6, 9, 2, 5, 9, 5, 6, 7, 9, - 4, 2, 6, 4, 3, 4, 4, 5, 5, 7, 3, 7, 4, - 2, 6, 2, 2, 5, 5, 6, 7, 5, 10, 7, 8, 5, - 8, 3, 6, 3, 9, 5, 4, 9, 4, 11, 7, 5, 6, - 6, 5, 6, 6, 4, 4, 5, - }; - static const unsigned short int aOffset[98] = { - 0, 5, 10, 13, 16, 16, 19, 25, 31, 36, 43, 45, 52, - 56, 61, 68, 74, 82, 92, 98, 103, 111, 118, 128, 136, 142, - 146, 152, 160, 164, 168, 172, 175, 181, 190, 197, 201, 201, 208, - 212, 216, 220, 225, 231, 237, 246, 246, 251, 260, 265, 271, 278, - 287, 291, 291, 297, 301, 304, 308, 312, 317, 322, 329, 329, 336, - 340, 340, 346, 348, 348, 353, 358, 364, 371, 376, 386, 393, 401, - 406, 414, 417, 423, 426, 435, 440, 440, 449, 453, 464, 471, 476, - 482, 488, 493, 499, 505, 509, 513, - }; - static const unsigned char aCode[98] = { - TK_ABORT, TK_AFTER, TK_ALL, TK_AND, TK_AS, - TK_ASC, TK_ATTACH, TK_BEFORE, TK_BEGIN, TK_BETWEEN, - TK_BY, TK_CASCADE, TK_CASE, TK_CHECK, TK_COLLATE, - TK_COMMIT, TK_CONFLICT, TK_CONSTRAINT, TK_CREATE, TK_JOIN_KW, - TK_DATABASE, TK_DEFAULT, TK_DEFERRABLE, TK_DEFERRED, TK_DELETE, - TK_DESC, TK_DETACH, TK_DISTINCT, TK_DROP, TK_EACH, - TK_ELSE, TK_END, TK_EXCEPT, TK_EXCLUSIVE, TK_EXPLAIN, - TK_FAIL, TK_FOR, TK_FOREIGN, TK_FROM, TK_JOIN_KW, - TK_GLOB, TK_GROUP, TK_HAVING, TK_IGNORE, TK_IMMEDIATE, - TK_IN, TK_INDEX, TK_INITIALLY, TK_JOIN_KW, TK_INSERT, - TK_INSTEAD, TK_INTERSECT, TK_INTO, TK_IS, TK_ISNULL, - TK_JOIN, TK_KEY, TK_JOIN_KW, TK_LIKE, TK_LIMIT, - TK_MATCH, TK_JOIN_KW, TK_NOT, TK_NOTNULL, TK_NULL, - TK_OF, TK_OFFSET, TK_ON, TK_OR, TK_ORDER, - TK_JOIN_KW, TK_PRAGMA, TK_PRIMARY, TK_RAISE, TK_REFERENCES, - TK_REPLACE, TK_RESTRICT, TK_JOIN_KW, TK_ROLLBACK, TK_ROW, - TK_SELECT, TK_SET, TK_STATEMENT, TK_TABLE, TK_TEMP, - TK_TEMP, TK_THEN, TK_TRANSACTION,TK_TRIGGER, TK_UNION, - TK_UNIQUE, TK_UPDATE, TK_USING, TK_VACUUM, TK_VALUES, - TK_VIEW, TK_WHEN, TK_WHERE, - }; - int h, i; - if( n<2 ) return TK_ID; - h = (sqlite3UpperToLower[((unsigned char*)z)[0]]*5 + - sqlite3UpperToLower[((unsigned char*)z)[n-1]]*3 + - n) % 154; - for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){ - if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){ - return aCode[i]; - } - } - return TK_ID; -} - -/* -** If X is a character that can be used in an identifier and -** X&0x80==0 then isIdChar[X] will be 1. If X&0x80==0x80 then -** X is always an identifier character. (Hence all UTF-8 -** characters can be part of an identifier). isIdChar[X] will -** be 0 for every character in the lower 128 ASCII characters -** that cannot be used as part of an identifier. -** -** In this implementation, an identifier can be a string of -** alphabetic characters, digits, and "_" plus any character -** with the high-order bit set. The latter rule means that -** any sequence of UTF-8 characters or characters taken from -** an extended ISO8859 character set can form an identifier. -*/ -static const char isIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; - -#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x2f && isIdChar[c-0x30])) - -/* -** Return the length of the token that begins at z[0]. -** Store the token type in *tokenType before returning. -*/ -static int sqliteGetToken(const unsigned char *z, int *tokenType){ - int i, c; - switch( *z ){ - case ' ': case '\t': case '\n': case '\f': case '\r': { - for(i=1; isspace(z[i]); i++){} - *tokenType = TK_SPACE; - return i; - } - case '-': { - if( z[1]=='-' ){ - for(i=2; (c=z[i])!=0 && c!='\n'; i++){} - *tokenType = TK_COMMENT; - return i; - } - *tokenType = TK_MINUS; - return 1; - } - case '(': { - *tokenType = TK_LP; - return 1; - } - case ')': { - *tokenType = TK_RP; - return 1; - } - case ';': { - *tokenType = TK_SEMI; - return 1; - } - case '+': { - *tokenType = TK_PLUS; - return 1; - } - case '*': { - *tokenType = TK_STAR; - return 1; - } - case '/': { - if( z[1]!='*' || z[2]==0 ){ - *tokenType = TK_SLASH; - return 1; - } - for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} - if( c ) i++; - *tokenType = TK_COMMENT; - return i; - } - case '%': { - *tokenType = TK_REM; - return 1; - } - case '=': { - *tokenType = TK_EQ; - return 1 + (z[1]=='='); - } - case '<': { - if( (c=z[1])=='=' ){ - *tokenType = TK_LE; - return 2; - }else if( c=='>' ){ - *tokenType = TK_NE; - return 2; - }else if( c=='<' ){ - *tokenType = TK_LSHIFT; - return 2; - }else{ - *tokenType = TK_LT; - return 1; - } - } - case '>': { - if( (c=z[1])=='=' ){ - *tokenType = TK_GE; - return 2; - }else if( c=='>' ){ - *tokenType = TK_RSHIFT; - return 2; - }else{ - *tokenType = TK_GT; - return 1; - } - } - case '!': { - if( z[1]!='=' ){ - *tokenType = TK_ILLEGAL; - return 2; - }else{ - *tokenType = TK_NE; - return 2; - } - } - case '|': { - if( z[1]!='|' ){ - *tokenType = TK_BITOR; - return 1; - }else{ - *tokenType = TK_CONCAT; - return 2; - } - } - case ',': { - *tokenType = TK_COMMA; - return 1; - } - case '&': { - *tokenType = TK_BITAND; - return 1; - } - case '~': { - *tokenType = TK_BITNOT; - return 1; - } - case '\'': case '"': { - int delim = z[0]; - for(i=1; (c=z[i])!=0; i++){ - if( c==delim ){ - if( z[i+1]==delim ){ - i++; - }else{ - break; - } - } - } - if( c ) i++; - *tokenType = TK_STRING; - return i; - } - case '.': { - *tokenType = TK_DOT; - return 1; - } - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - *tokenType = TK_INTEGER; - for(i=1; isdigit(z[i]); i++){} - if( z[i]=='.' && isdigit(z[i+1]) ){ - i += 2; - while( isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } - if( (z[i]=='e' || z[i]=='E') && - ( isdigit(z[i+1]) - || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) - ) - ){ - i += 2; - while( isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } - return i; - } - case '[': { - for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} - *tokenType = TK_ID; - return i; - } - case '?': { - *tokenType = TK_VARIABLE; - for(i=1; isdigit(z[i]); i++){} - return i; - } - case ':': { - for(i=1; IdChar(z[i]); i++){} - *tokenType = i>1 ? TK_VARIABLE : TK_ILLEGAL; - return i; - } - case '$': { - *tokenType = TK_VARIABLE; - if( z[1]=='{' ){ - int nBrace = 1; - for(i=2; (c=z[i])!=0 && nBrace; i++){ - if( c=='{' ){ - nBrace++; - }else if( c=='}' ){ - nBrace--; - } - } - if( c==0 ) *tokenType = TK_ILLEGAL; - }else{ - int n = 0; - for(i=1; (c=z[i])!=0; i++){ - if( isalnum(c) || c=='_' ){ - n++; - }else if( c=='(' && n>0 ){ - do{ - i++; - }while( (c=z[i])!=0 && !isspace(c) && c!=')' ); - if( c==')' ){ - i++; - }else{ - *tokenType = TK_ILLEGAL; - } - break; - }else if( c==':' && z[i+1]==':' ){ - i++; - }else{ - break; - } - } - if( n==0 ) *tokenType = TK_ILLEGAL; - } - return i; - } - case 'x': case 'X': { - if( (c=z[1])=='\'' || c=='"' ){ - int delim = c; - *tokenType = TK_BLOB; - for(i=2; (c=z[i])!=0; i++){ - if( c==delim ){ - if( i%2 ) *tokenType = TK_ILLEGAL; - break; - } - if( !isxdigit(c) ){ - *tokenType = TK_ILLEGAL; - return i; - } - } - if( c ) i++; - return i; - } - /* Otherwise fall through to the next case */ - } - default: { - if( !IdChar(*z) ){ - break; - } - for(i=1; IdChar(z[i]); i++){} - *tokenType = sqlite3KeywordCode((char*)z, i); - return i; - } - } - *tokenType = TK_ILLEGAL; - return 1; -} - -/* -** Run the parser on the given SQL string. The parser structure is -** passed in. An SQLITE_ status code is returned. If an error occurs -** and pzErrMsg!=NULL then an error message might be written into -** memory obtained from malloc() and *pzErrMsg made to point to that -** error message. Or maybe not. -*/ -int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ - int nErr = 0; - int i; - void *pEngine; - int tokenType; - int lastTokenParsed = -1; - sqlite3 *db = pParse->db; - extern void *sqlite3ParserAlloc(void*(*)(int)); - extern void sqlite3ParserFree(void*, void(*)(void*)); - extern int sqlite3Parser(void*, int, Token, Parse*); - - db->flags &= ~SQLITE_Interrupt; - pParse->rc = SQLITE_OK; - i = 0; - pEngine = sqlite3ParserAlloc((void*(*)(int))malloc); - if( pEngine==0 ){ - sqlite3SetString(pzErrMsg, "out of memory", (char*)0); - return 1; - } - assert( pParse->sLastToken.dyn==0 ); - assert( pParse->pNewTable==0 ); - assert( pParse->pNewTrigger==0 ); - assert( pParse->nVar==0 ); - assert( pParse->nVarExpr==0 ); - assert( pParse->nVarExprAlloc==0 ); - assert( pParse->apVarExpr==0 ); - pParse->zTail = pParse->zSql = zSql; - while( sqlite3_malloc_failed==0 && zSql[i]!=0 ){ - assert( i>=0 ); - pParse->sLastToken.z = &zSql[i]; - assert( pParse->sLastToken.dyn==0 ); - pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType); - i += pParse->sLastToken.n; - switch( tokenType ){ - case TK_SPACE: - case TK_COMMENT: { - if( (db->flags & SQLITE_Interrupt)!=0 ){ - pParse->rc = SQLITE_INTERRUPT; - sqlite3SetString(pzErrMsg, "interrupt", (char*)0); - goto abort_parse; - } - break; - } - case TK_ILLEGAL: { - if( pzErrMsg ){ - sqliteFree(*pzErrMsg); - *pzErrMsg = sqlite3MPrintf("unrecognized token: \"%T\"", - &pParse->sLastToken); - } - nErr++; - goto abort_parse; - } - case TK_SEMI: { - pParse->zTail = &zSql[i]; - /* Fall thru into the default case */ - } - default: { - sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse); - lastTokenParsed = tokenType; - if( pParse->rc!=SQLITE_OK ){ - goto abort_parse; - } - break; - } - } - } -abort_parse: - if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){ - if( lastTokenParsed!=TK_SEMI ){ - sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); - pParse->zTail = &zSql[i]; - } - sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); - } - sqlite3ParserFree(pEngine, free); - if( sqlite3_malloc_failed ){ - pParse->rc = SQLITE_NOMEM; - } - if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ - sqlite3SetString(&pParse->zErrMsg, sqlite3ErrStr(pParse->rc), - (char*)0); - } - if( pParse->zErrMsg ){ - if( pzErrMsg && *pzErrMsg==0 ){ - *pzErrMsg = pParse->zErrMsg; - }else{ - sqliteFree(pParse->zErrMsg); - } - pParse->zErrMsg = 0; - if( !nErr ) nErr++; - } - if( pParse->pVdbe && pParse->nErr>0 ){ - sqlite3VdbeDelete(pParse->pVdbe); - pParse->pVdbe = 0; - } - sqlite3DeleteTable(pParse->db, pParse->pNewTable); - sqlite3DeleteTrigger(pParse->pNewTrigger); - sqliteFree(pParse->apVarExpr); - if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){ - pParse->rc = SQLITE_ERROR; - } - return nErr; -} - -/* -** Token types used by the sqlite3_complete() routine. See the header -** comments on that procedure for additional information. -*/ -#define tkEXPLAIN 0 -#define tkCREATE 1 -#define tkTEMP 2 -#define tkTRIGGER 3 -#define tkEND 4 -#define tkSEMI 5 -#define tkWS 6 -#define tkOTHER 7 - -/* -** Return TRUE if the given SQL string ends in a semicolon. -** -** Special handling is require for CREATE TRIGGER statements. -** Whenever the CREATE TRIGGER keywords are seen, the statement -** must end with ";END;". -** -** This implementation uses a state machine with 7 states: -** -** (0) START At the beginning or end of an SQL statement. This routine -** returns 1 if it ends in the START state and 0 if it ends -** in any other state. -** -** (1) EXPLAIN The keyword EXPLAIN has been seen at the beginning of -** a statement. -** -** (2) CREATE The keyword CREATE has been seen at the beginning of a -** statement, possibly preceeded by EXPLAIN and/or followed by -** TEMP or TEMPORARY -** -** (3) NORMAL We are in the middle of statement which ends with a single -** semicolon. -** -** (4) TRIGGER We are in the middle of a trigger definition that must be -** ended by a semicolon, the keyword END, and another semicolon. -** -** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at -** the end of a trigger definition. -** -** (6) END We've seen the ";END" of the ";END;" that occurs at the end -** of a trigger difinition. -** -** Transitions between states above are determined by tokens extracted -** from the input. The following tokens are significant: -** -** (0) tkEXPLAIN The "explain" keyword. -** (1) tkCREATE The "create" keyword. -** (2) tkTEMP The "temp" or "temporary" keyword. -** (3) tkTRIGGER The "trigger" keyword. -** (4) tkEND The "end" keyword. -** (5) tkSEMI A semicolon. -** (6) tkWS Whitespace -** (7) tkOTHER Any other SQL token. -** -** Whitespace never causes a state transition and is always ignored. -*/ -int sqlite3_complete(const char *zSql){ - u8 state = 0; /* Current state, using numbers defined in header comment */ - u8 token; /* Value of the next token */ - - /* The following matrix defines the transition from one state to another - ** according to what token is seen. trans[state][token] returns the - ** next state. - */ - static const u8 trans[7][8] = { - /* Token: */ - /* State: ** EXPLAIN CREATE TEMP TRIGGER END SEMI WS OTHER */ - /* 0 START: */ { 1, 2, 3, 3, 3, 0, 0, 3, }, - /* 1 EXPLAIN: */ { 3, 2, 3, 3, 3, 0, 1, 3, }, - /* 2 CREATE: */ { 3, 3, 2, 4, 3, 0, 2, 3, }, - /* 3 NORMAL: */ { 3, 3, 3, 3, 3, 0, 3, 3, }, - /* 4 TRIGGER: */ { 4, 4, 4, 4, 4, 5, 4, 4, }, - /* 5 SEMI: */ { 4, 4, 4, 4, 6, 5, 5, 4, }, - /* 6 END: */ { 4, 4, 4, 4, 4, 0, 6, 4, }, - }; - - while( *zSql ){ - switch( *zSql ){ - case ';': { /* A semicolon */ - token = tkSEMI; - break; - } - case ' ': - case '\r': - case '\t': - case '\n': - case '\f': { /* White space is ignored */ - token = tkWS; - break; - } - case '/': { /* C-style comments */ - if( zSql[1]!='*' ){ - token = tkOTHER; - break; - } - zSql += 2; - while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } - if( zSql[0]==0 ) return 0; - zSql++; - token = tkWS; - break; - } - case '-': { /* SQL-style comments from "--" to end of line */ - if( zSql[1]!='-' ){ - token = tkOTHER; - break; - } - while( *zSql && *zSql!='\n' ){ zSql++; } - if( *zSql==0 ) return state==0; - token = tkWS; - break; - } - case '[': { /* Microsoft-style identifiers in [...] */ - zSql++; - while( *zSql && *zSql!=']' ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - case '"': /* single- and double-quoted strings */ - case '\'': { - int c = *zSql; - zSql++; - while( *zSql && *zSql!=c ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - default: { - int c; - if( IdChar((u8)*zSql) ){ - /* Keywords and unquoted identifiers */ - int nId; - for(nId=1; IdChar(zSql[nId]); nId++){} - switch( *zSql ){ - case 'c': case 'C': { - if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ - token = tkCREATE; - }else{ - token = tkOTHER; - } - break; - } - case 't': case 'T': { - if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ - token = tkTRIGGER; - }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ - token = tkTEMP; - }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ - token = tkTEMP; - }else{ - token = tkOTHER; - } - break; - } - case 'e': case 'E': { - if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ - token = tkEND; - }else if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ - token = tkEXPLAIN; - }else{ - token = tkOTHER; - } - break; - } - default: { - token = tkOTHER; - break; - } - } - zSql += nId-1; - }else{ - /* Operators and special symbols */ - token = tkOTHER; - } - break; - } - } - state = trans[state][token]; - zSql++; - } - return state==0; -} - -/* -** This routine is the same as the sqlite3_complete() routine described -** above, except that the parameter is required to be UTF-16 encoded, not -** UTF-8. -*/ -int sqlite3_complete16(const void *zSql){ - sqlite3_value *pVal; - char const *zSql8; - int rc = 0; - - pVal = sqlite3ValueNew(); - sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8); - if( zSql8 ){ - rc = sqlite3_complete(zSql8); - } - sqlite3ValueFree(pVal); - return rc; -} diff --git a/kopete/plugins/statistics/sqlite/trigger.c b/kopete/plugins/statistics/sqlite/trigger.c deleted file mode 100644 index bbb526f8..00000000 --- a/kopete/plugins/statistics/sqlite/trigger.c +++ /dev/null @@ -1,804 +0,0 @@ -/* -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -* -*/ -#include "sqliteInt.h" - -/* -** Delete a linked list of TriggerStep structures. -*/ -void sqlite3DeleteTriggerStep(TriggerStep *pTriggerStep){ - while( pTriggerStep ){ - TriggerStep * pTmp = pTriggerStep; - pTriggerStep = pTriggerStep->pNext; - - if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z); - sqlite3ExprDelete(pTmp->pWhere); - sqlite3ExprListDelete(pTmp->pExprList); - sqlite3SelectDelete(pTmp->pSelect); - sqlite3IdListDelete(pTmp->pIdList); - - sqliteFree(pTmp); - } -} - -/* -** This is called by the parser when it sees a CREATE TRIGGER statement -** up to the point of the BEGIN before the trigger actions. A Trigger -** structure is generated based on the information available and stored -** in pParse->pNewTrigger. After the trigger actions have been parsed, the -** sqlite3FinishTrigger() function is called to complete the trigger -** construction process. -*/ -void sqlite3BeginTrigger( - Parse *pParse, /* The parse context of the CREATE TRIGGER statement */ - Token *pName1, /* The name of the trigger */ - Token *pName2, /* The name of the trigger */ - int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ - int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ - IdList *pColumns, /* column list if this is an UPDATE OF trigger */ - SrcList *pTableName,/* The name of the table/view the trigger applies to */ - int foreach, /* One of TK_ROW or TK_STATEMENT */ - Expr *pWhen, /* WHEN clause */ - int isTemp /* True if the TEMPORARY keyword is present */ -){ - Trigger *pTrigger; - Table *pTab; - char *zName = 0; /* Name of the trigger */ - sqlite3 *db = pParse->db; - int iDb; /* The database to store the trigger in */ - Token *pName; /* The unqualified db name */ - DbFixer sFix; - - if( isTemp ){ - /* If TEMP was specified, then the trigger name may not be qualified. */ - if( pName2 && pName2->n>0 ){ - sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name"); - goto trigger_cleanup; - } - iDb = 1; - pName = pName1; - }else{ - /* Figure out the db that the the trigger will be created in */ - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ){ - goto trigger_cleanup; - } - } - - /* If the trigger name was unqualified, and the table is a temp table, - ** then set iDb to 1 to create the trigger in the temporary database. - ** If sqlite3SrcListLookup() returns 0, indicating the table does not - ** exist, the error is caught by the block below. - */ - if( !pTableName || sqlite3_malloc_failed ) goto trigger_cleanup; - pTab = sqlite3SrcListLookup(pParse, pTableName); - if( pName2->n==0 && pTab && pTab->iDb==1 ){ - iDb = 1; - } - - /* Ensure the table name matches database name and that the table exists */ - if( sqlite3_malloc_failed ) goto trigger_cleanup; - assert( pTableName->nSrc==1 ); - if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) && - sqlite3FixSrcList(&sFix, pTableName) ){ - goto trigger_cleanup; - } - pTab = sqlite3SrcListLookup(pParse, pTableName); - if( !pTab ){ - /* The table does not exist. */ - goto trigger_cleanup; - } - - /* Check that the trigger name is not reserved and that no trigger of the - ** specified name exists */ - zName = sqlite3NameFromToken(pName); - if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto trigger_cleanup; - } - if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){ - sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); - goto trigger_cleanup; - } - - /* Do not create a trigger on a system table */ - if( (iDb!=1 && sqlite3StrICmp(pTab->zName, MASTER_NAME)==0) || - (iDb==1 && sqlite3StrICmp(pTab->zName, TEMP_MASTER_NAME)==0) - ){ - sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); - pParse->nErr++; - goto trigger_cleanup; - } - - /* INSTEAD of triggers are only for views and views only support INSTEAD - ** of triggers. - */ - if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", - (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); - goto trigger_cleanup; - } - if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" - " trigger on table: %S", pTableName, 0); - goto trigger_cleanup; - } - -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_CREATE_TRIGGER; - const char *zDb = db->aDb[pTab->iDb].zName; - const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb; - if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){ - goto trigger_cleanup; - } - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb),0,zDb)){ - goto trigger_cleanup; - } - } -#endif - - /* INSTEAD OF triggers can only appear on views and BEFORE triggers - ** cannot appear on views. So we might as well translate every - ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code - ** elsewhere. - */ - if (tr_tm == TK_INSTEAD){ - tr_tm = TK_BEFORE; - } - - /* Build the Trigger object */ - pTrigger = (Trigger*)sqliteMalloc(sizeof(Trigger)); - if( pTrigger==0 ) goto trigger_cleanup; - pTrigger->name = zName; - zName = 0; - pTrigger->table = sqliteStrDup(pTableName->a[0].zName); - if( sqlite3_malloc_failed ) goto trigger_cleanup; - pTrigger->iDb = iDb; - pTrigger->iTabDb = pTab->iDb; - pTrigger->op = op; - pTrigger->tr_tm = tr_tm; - pTrigger->pWhen = sqlite3ExprDup(pWhen); - pTrigger->pColumns = sqlite3IdListDup(pColumns); - pTrigger->foreach = foreach; - sqlite3TokenCopy(&pTrigger->nameToken,pName); - assert( pParse->pNewTrigger==0 ); - pParse->pNewTrigger = pTrigger; - -trigger_cleanup: - sqliteFree(zName); - sqlite3SrcListDelete(pTableName); - sqlite3IdListDelete(pColumns); - sqlite3ExprDelete(pWhen); -} - -/* -** This routine is called after all of the trigger actions have been parsed -** in order to complete the process of building the trigger. -*/ -void sqlite3FinishTrigger( - Parse *pParse, /* Parser context */ - TriggerStep *pStepList, /* The triggered program */ - Token *pAll /* Token that describes the complete CREATE TRIGGER */ -){ - Trigger *nt = 0; /* The trigger whose construction is finishing up */ - sqlite3 *db = pParse->db; /* The database */ - DbFixer sFix; - - if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup; - nt = pParse->pNewTrigger; - pParse->pNewTrigger = 0; - nt->step_list = pStepList; - while( pStepList ){ - pStepList->pTrig = nt; - pStepList = pStepList->pNext; - } - if( sqlite3FixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken) - && sqlite3FixTriggerStep(&sFix, nt->step_list) ){ - goto triggerfinish_cleanup; - } - - /* if we are not initializing, and this trigger is not on a TEMP table, - ** build the sqlite_master entry - */ - if( !db->init.busy ){ - static const VdbeOpList insertTrig[] = { - { OP_NewRecno, 0, 0, 0 }, - { OP_String8, 0, 0, "trigger" }, - { OP_String8, 0, 0, 0 }, /* 2: trigger name */ - { OP_String8, 0, 0, 0 }, /* 3: table name */ - { OP_Integer, 0, 0, 0 }, - { OP_String8, 0, 0, "CREATE TRIGGER "}, - { OP_String8, 0, 0, 0 }, /* 6: SQL */ - { OP_Concat, 0, 0, 0 }, - { OP_MakeRecord, 5, 0, "tttit" }, - { OP_PutIntKey, 0, 0, 0 }, - }; - int addr; - Vdbe *v; - - /* Make an entry in the sqlite_master table */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto triggerfinish_cleanup; - sqlite3BeginWriteOperation(pParse, 0, nt->iDb); - sqlite3OpenMasterTable(v, nt->iDb); - addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig); - sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); - sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); - sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n); - if( nt->iDb!=0 ){ - sqlite3ChangeCookie(db, v, nt->iDb); - } - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeOp3(v, OP_ParseSchema, nt->iDb, 0, - sqlite3MPrintf("type='trigger' AND name='%q'", nt->name), P3_DYNAMIC); - } - - if( db->init.busy ){ - Table *pTab; - sqlite3HashInsert(&db->aDb[nt->iDb].trigHash, - nt->name, strlen(nt->name)+1, nt); - pTab = sqlite3LocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName); - assert( pTab!=0 ); - nt->pNext = pTab->pTrigger; - pTab->pTrigger = nt; - nt = 0; - } - -triggerfinish_cleanup: - sqlite3DeleteTrigger(nt); - sqlite3DeleteTrigger(pParse->pNewTrigger); - pParse->pNewTrigger = 0; - sqlite3DeleteTriggerStep(pStepList); -} - -/* -** Make a copy of all components of the given trigger step. This has -** the effect of copying all Expr.token.z values into memory obtained -** from sqliteMalloc(). As initially created, the Expr.token.z values -** all point to the input string that was fed to the parser. But that -** string is ephemeral - it will go away as soon as the sqlite3_exec() -** call that started the parser exits. This routine makes a persistent -** copy of all the Expr.token.z strings so that the TriggerStep structure -** will be valid even after the sqlite3_exec() call returns. -*/ -static void sqlitePersistTriggerStep(TriggerStep *p){ - if( p->target.z ){ - p->target.z = sqliteStrNDup(p->target.z, p->target.n); - p->target.dyn = 1; - } - if( p->pSelect ){ - Select *pNew = sqlite3SelectDup(p->pSelect); - sqlite3SelectDelete(p->pSelect); - p->pSelect = pNew; - } - if( p->pWhere ){ - Expr *pNew = sqlite3ExprDup(p->pWhere); - sqlite3ExprDelete(p->pWhere); - p->pWhere = pNew; - } - if( p->pExprList ){ - ExprList *pNew = sqlite3ExprListDup(p->pExprList); - sqlite3ExprListDelete(p->pExprList); - p->pExprList = pNew; - } - if( p->pIdList ){ - IdList *pNew = sqlite3IdListDup(p->pIdList); - sqlite3IdListDelete(p->pIdList); - p->pIdList = pNew; - } -} - -/* -** Turn a SELECT statement (that the pSelect parameter points to) into -** a trigger step. Return a pointer to a TriggerStep structure. -** -** The parser calls this routine when it finds a SELECT statement in -** body of a TRIGGER. -*/ -TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){ - TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); - if( pTriggerStep==0 ) return 0; - - pTriggerStep->op = TK_SELECT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->orconf = OE_Default; - sqlitePersistTriggerStep(pTriggerStep); - - return pTriggerStep; -} - -/* -** Build a trigger step out of an INSERT statement. Return a pointer -** to the new trigger step. -** -** The parser calls this routine when it sees an INSERT inside the -** body of a trigger. -*/ -TriggerStep *sqlite3TriggerInsertStep( - Token *pTableName, /* Name of the table into which we insert */ - IdList *pColumn, /* List of columns in pTableName to insert into */ - ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ - Select *pSelect, /* A SELECT statement that supplies values */ - int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ -){ - TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); - if( pTriggerStep==0 ) return 0; - - assert(pEList == 0 || pSelect == 0); - assert(pEList != 0 || pSelect != 0); - - pTriggerStep->op = TK_INSERT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->target = *pTableName; - pTriggerStep->pIdList = pColumn; - pTriggerStep->pExprList = pEList; - pTriggerStep->orconf = orconf; - sqlitePersistTriggerStep(pTriggerStep); - - return pTriggerStep; -} - -/* -** Construct a trigger step that implements an UPDATE statement and return -** a pointer to that trigger step. The parser calls this routine when it -** sees an UPDATE statement inside the body of a CREATE TRIGGER. -*/ -TriggerStep *sqlite3TriggerUpdateStep( - Token *pTableName, /* Name of the table to be updated */ - ExprList *pEList, /* The SET clause: list of column and new values */ - Expr *pWhere, /* The WHERE clause */ - int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ -){ - TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); - if( pTriggerStep==0 ) return 0; - - pTriggerStep->op = TK_UPDATE; - pTriggerStep->target = *pTableName; - pTriggerStep->pExprList = pEList; - pTriggerStep->pWhere = pWhere; - pTriggerStep->orconf = orconf; - sqlitePersistTriggerStep(pTriggerStep); - - return pTriggerStep; -} - -/* -** Construct a trigger step that implements a DELETE statement and return -** a pointer to that trigger step. The parser calls this routine when it -** sees a DELETE statement inside the body of a CREATE TRIGGER. -*/ -TriggerStep *sqlite3TriggerDeleteStep(Token *pTableName, Expr *pWhere){ - TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); - if( pTriggerStep==0 ) return 0; - - pTriggerStep->op = TK_DELETE; - pTriggerStep->target = *pTableName; - pTriggerStep->pWhere = pWhere; - pTriggerStep->orconf = OE_Default; - sqlitePersistTriggerStep(pTriggerStep); - - return pTriggerStep; -} - -/* -** Recursively delete a Trigger structure -*/ -void sqlite3DeleteTrigger(Trigger *pTrigger){ - if( pTrigger==0 ) return; - sqlite3DeleteTriggerStep(pTrigger->step_list); - sqliteFree(pTrigger->name); - sqliteFree(pTrigger->table); - sqlite3ExprDelete(pTrigger->pWhen); - sqlite3IdListDelete(pTrigger->pColumns); - if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z); - sqliteFree(pTrigger); -} - -/* -** This function is called to drop a trigger from the database schema. -** -** This may be called directly from the parser and therefore identifies -** the trigger by name. The sqlite3DropTriggerPtr() routine does the -** same job as this routine except it takes a pointer to the trigger -** instead of the trigger name. -**/ -void sqlite3DropTrigger(Parse *pParse, SrcList *pName){ - Trigger *pTrigger = 0; - int i; - const char *zDb; - const char *zName; - int nName; - sqlite3 *db = pParse->db; - - if( sqlite3_malloc_failed ) goto drop_trigger_cleanup; - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto drop_trigger_cleanup; - } - - assert( pName->nSrc==1 ); - zDb = pName->a[0].zDatabase; - zName = pName->a[0].zName; - nName = strlen(zName); - for(i=0; i<db->nDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; - pTrigger = sqlite3HashFind(&(db->aDb[j].trigHash), zName, nName+1); - if( pTrigger ) break; - } - if( !pTrigger ){ - sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); - goto drop_trigger_cleanup; - } - sqlite3DropTriggerPtr(pParse, pTrigger, 0); - -drop_trigger_cleanup: - sqlite3SrcListDelete(pName); -} - -/* -** Return a pointer to the Table structure for the table that a trigger -** is set on. -*/ -static Table *tableOfTrigger(sqlite3 *db, Trigger *pTrigger){ - return sqlite3FindTable(db,pTrigger->table,db->aDb[pTrigger->iTabDb].zName); -} - - -/* -** Drop a trigger given a pointer to that trigger. If nested is false, -** then also generate code to remove the trigger from the SQLITE_MASTER -** table. -*/ -void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ - Table *pTable; - Vdbe *v; - sqlite3 *db = pParse->db; - int iDb; - - iDb = pTrigger->iDb; - assert( iDb>=0 && iDb<db->nDb ); - pTable = tableOfTrigger(db, pTrigger); - assert(pTable); - assert( pTable->iDb==iDb || iDb==1 ); -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_DROP_TRIGGER; - const char *zDb = db->aDb[iDb].zName; - const char *zTab = SCHEMA_TABLE(iDb); - if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) || - sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ - return; - } - } -#endif - - /* Generate code to destroy the database record of the trigger. - */ - if( pTable!=0 && (v = sqlite3GetVdbe(pParse))!=0 ){ - int base; - static const VdbeOpList dropTrigger[] = { - { OP_Rewind, 0, ADDR(9), 0}, - { OP_String8, 0, 0, 0}, /* 1 */ - { OP_Column, 0, 1, 0}, - { OP_Ne, 0, ADDR(8), 0}, - { OP_String8, 0, 0, "trigger"}, - { OP_Column, 0, 0, 0}, - { OP_Ne, 0, ADDR(8), 0}, - { OP_Delete, 0, 0, 0}, - { OP_Next, 0, ADDR(1), 0}, /* 8 */ - }; - - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3OpenMasterTable(v, iDb); - base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0); - sqlite3ChangeCookie(db, v, iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrigger->name, 0); - } -} - -/* -** Remove a trigger from the hash tables of the sqlite* pointer. -*/ -void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ - Trigger *pTrigger; - int nName = strlen(zName); - pTrigger = sqlite3HashInsert(&(db->aDb[iDb].trigHash), zName, nName+1, 0); - if( pTrigger ){ - Table *pTable = tableOfTrigger(db, pTrigger); - assert( pTable!=0 ); - if( pTable->pTrigger == pTrigger ){ - pTable->pTrigger = pTrigger->pNext; - }else{ - Trigger *cc = pTable->pTrigger; - while( cc ){ - if( cc->pNext == pTrigger ){ - cc->pNext = cc->pNext->pNext; - break; - } - cc = cc->pNext; - } - assert(cc); - } - sqlite3DeleteTrigger(pTrigger); - db->flags |= SQLITE_InternChanges; - } -} - -/* -** pEList is the SET clause of an UPDATE statement. Each entry -** in pEList is of the format <id>=<expr>. If any of the entries -** in pEList have an <id> which matches an identifier in pIdList, -** then return TRUE. If pIdList==NULL, then it is considered a -** wildcard that matches anything. Likewise if pEList==NULL then -** it matches anything so always return true. Return false only -** if there is no match. -*/ -static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){ - int e; - if( !pIdList || !pEList ) return 1; - for(e=0; e<pEList->nExpr; e++){ - if( sqlite3IdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1; - } - return 0; -} - -/* A global variable that is TRUE if we should always set up temp tables for - * for triggers, even if there are no triggers to code. This is used to test - * how much overhead the triggers algorithm is causing. - * - * This flag can be set or cleared using the "trigger_overhead_test" pragma. - * The pragma is not documented since it is not really part of the interface - * to SQLite, just the test procedure. -*/ -int sqlite3_always_code_trigger_setup = 0; - -/* - * Returns true if a trigger matching op, tr_tm and foreach that is NOT already - * on the Parse objects trigger-stack (to prevent recursive trigger firing) is - * found in the list specified as pTrigger. - */ -int sqlite3TriggersExist( - Parse *pParse, /* Used to check for recursive triggers */ - Trigger *pTrigger, /* A list of triggers associated with a table */ - int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ - int tr_tm, /* one of TK_BEFORE, TK_AFTER */ - int foreach, /* one of TK_ROW or TK_STATEMENT */ - ExprList *pChanges /* Columns that change in an UPDATE statement */ -){ - Trigger * pTriggerCursor; - - if( sqlite3_always_code_trigger_setup ){ - return 1; - } - - pTriggerCursor = pTrigger; - while( pTriggerCursor ){ - if( pTriggerCursor->op == op && - pTriggerCursor->tr_tm == tr_tm && - pTriggerCursor->foreach == foreach && - checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){ - TriggerStack * ss; - ss = pParse->trigStack; - while( ss && ss->pTrigger != pTrigger ){ - ss = ss->pNext; - } - if( !ss )return 1; - } - pTriggerCursor = pTriggerCursor->pNext; - } - - return 0; -} - -/* -** Convert the pStep->target token into a SrcList and return a pointer -** to that SrcList. -** -** This routine adds a specific database name, if needed, to the target when -** forming the SrcList. This prevents a trigger in one database from -** referring to a target in another database. An exception is when the -** trigger is in TEMP in which case it can refer to any other database it -** wants. -*/ -static SrcList *targetSrcList( - Parse *pParse, /* The parsing context */ - TriggerStep *pStep /* The trigger containing the target token */ -){ - Token sDb; /* Dummy database name token */ - int iDb; /* Index of the database to use */ - SrcList *pSrc; /* SrcList to be returned */ - - iDb = pStep->pTrig->iDb; - if( iDb==0 || iDb>=2 ){ - assert( iDb<pParse->db->nDb ); - sDb.z = pParse->db->aDb[iDb].zName; - sDb.n = strlen(sDb.z); - pSrc = sqlite3SrcListAppend(0, &sDb, &pStep->target); - } else { - pSrc = sqlite3SrcListAppend(0, &pStep->target, 0); - } - return pSrc; -} - -/* -** Generate VDBE code for zero or more statements inside the body of a -** trigger. -*/ -static int codeTriggerProgram( - Parse *pParse, /* The parser context */ - TriggerStep *pStepList, /* List of statements inside the trigger body */ - int orconfin /* Conflict algorithm. (OE_Abort, etc) */ -){ - TriggerStep * pTriggerStep = pStepList; - int orconf; - Vdbe *v = pParse->pVdbe; - - assert( pTriggerStep!=0 ); - assert( v!=0 ); - sqlite3VdbeAddOp(v, OP_ContextPush, 0, 0); - VdbeComment((v, "# begin trigger %s", pStepList->pTrig->name)); - while( pTriggerStep ){ - orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; - pParse->trigStack->orconf = orconf; - switch( pTriggerStep->op ){ - case TK_SELECT: { - Select * ss = sqlite3SelectDup(pTriggerStep->pSelect); - assert(ss); - assert(ss->pSrc); - sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0); - sqlite3SelectDelete(ss); - break; - } - case TK_UPDATE: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0); - sqlite3Update(pParse, pSrc, - sqlite3ExprListDup(pTriggerStep->pExprList), - sqlite3ExprDup(pTriggerStep->pWhere), orconf); - sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0); - break; - } - case TK_INSERT: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0); - sqlite3Insert(pParse, pSrc, - sqlite3ExprListDup(pTriggerStep->pExprList), - sqlite3SelectDup(pTriggerStep->pSelect), - sqlite3IdListDup(pTriggerStep->pIdList), orconf); - sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0); - break; - } - case TK_DELETE: { - SrcList *pSrc; - sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0); - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3DeleteFrom(pParse, pSrc, sqlite3ExprDup(pTriggerStep->pWhere)); - sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0); - break; - } - default: - assert(0); - } - pTriggerStep = pTriggerStep->pNext; - } - sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0); - VdbeComment((v, "# end trigger %s", pStepList->pTrig->name)); - - return 0; -} - -/* -** This is called to code FOR EACH ROW triggers. -** -** When the code that this function generates is executed, the following -** must be true: -** -** 1. No cursors may be open in the main database. (But newIdx and oldIdx -** can be indices of cursors in temporary tables. See below.) -** -** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then -** a temporary vdbe cursor (index newIdx) must be open and pointing at -** a row containing values to be substituted for new.* expressions in the -** trigger program(s). -** -** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then -** a temporary vdbe cursor (index oldIdx) must be open and pointing at -** a row containing values to be substituted for old.* expressions in the -** trigger program(s). -** -*/ -int sqlite3CodeRowTrigger( - Parse *pParse, /* Parse context */ - int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ - ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ - int tr_tm, /* One of TK_BEFORE, TK_AFTER */ - Table *pTab, /* The table to code triggers from */ - int newIdx, /* The indice of the "new" row to access */ - int oldIdx, /* The indice of the "old" row to access */ - int orconf, /* ON CONFLICT policy */ - int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ -){ - Trigger *pTrigger; - TriggerStack *pStack; - TriggerStack trigStackEntry; - - assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); - assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER ); - - assert(newIdx != -1 || oldIdx != -1); - - pTrigger = pTab->pTrigger; - while( pTrigger ){ - int fire_this = 0; - - /* determine whether we should code this trigger */ - if( pTrigger->op == op && pTrigger->tr_tm == tr_tm && - pTrigger->foreach == TK_ROW ){ - fire_this = 1; - for(pStack=pParse->trigStack; pStack; pStack=pStack->pNext){ - if( pStack->pTrigger==pTrigger ){ - fire_this = 0; - } - } - if( op == TK_UPDATE && pTrigger->pColumns && - !checkColumnOverLap(pTrigger->pColumns, pChanges) ){ - fire_this = 0; - } - } - - if( fire_this ){ - int endTrigger; - SrcList dummyTablist; - Expr * whenExpr; - AuthContext sContext; - - dummyTablist.nSrc = 0; - - /* Push an entry on to the trigger stack */ - trigStackEntry.pTrigger = pTrigger; - trigStackEntry.newIdx = newIdx; - trigStackEntry.oldIdx = oldIdx; - trigStackEntry.pTab = pTab; - trigStackEntry.pNext = pParse->trigStack; - trigStackEntry.ignoreJump = ignoreJump; - pParse->trigStack = &trigStackEntry; - sqlite3AuthContextPush(pParse, &sContext, pTrigger->name); - - /* code the WHEN clause */ - endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); - whenExpr = sqlite3ExprDup(pTrigger->pWhen); - if( sqlite3ExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){ - pParse->trigStack = trigStackEntry.pNext; - sqlite3ExprDelete(whenExpr); - return 1; - } - sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1); - sqlite3ExprDelete(whenExpr); - - codeTriggerProgram(pParse, pTrigger->step_list, orconf); - - /* Pop the entry off the trigger stack */ - pParse->trigStack = trigStackEntry.pNext; - sqlite3AuthContextPop(&sContext); - - sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); - } - pTrigger = pTrigger->pNext; - } - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/update.c b/kopete/plugins/statistics/sqlite/update.c deleted file mode 100644 index 08c7987c..00000000 --- a/kopete/plugins/statistics/sqlite/update.c +++ /dev/null @@ -1,450 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle UPDATE statements. -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** Process an UPDATE statement. -** -** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; -** \_______/ \________/ \______/ \________________/ -* onError pTabList pChanges pWhere -*/ -void sqlite3Update( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* The table in which we should change things */ - ExprList *pChanges, /* Things to be changed */ - Expr *pWhere, /* The WHERE clause. May be null */ - int onError /* How to handle constraint errors */ -){ - int i, j; /* Loop counters */ - Table *pTab; /* The table to be updated */ - int addr = 0; /* VDBE instruction address of the start of the loop */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ - Vdbe *v; /* The virtual database engine */ - Index *pIdx; /* For looping over indices */ - int nIdx; /* Number of indices that need updating */ - int nIdxTotal; /* Total number of indices */ - int iCur; /* VDBE Cursor number of pTab */ - sqlite3 *db; /* The database structure */ - Index **apIdx = 0; /* An array of indices that need updating too */ - char *aIdxUsed = 0; /* aIdxUsed[i]==1 if the i-th index is used */ - int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the - ** an expression for the i-th column of the table. - ** aXRef[i]==-1 if the i-th column is not changed. */ - int chngRecno; /* True if the record number is being changed */ - Expr *pRecnoExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ - int isView; /* Trying to update a view */ - AuthContext sContext; /* The authorization context */ - - int before_triggers; /* True if there are any BEFORE triggers */ - int after_triggers; /* True if there are any AFTER triggers */ - int row_triggers_exist = 0; /* True if any row triggers exist */ - - int newIdx = -1; /* index of trigger "new" temp table */ - int oldIdx = -1; /* index of trigger "old" temp table */ - - sContext.pParse = 0; - if( pParse->nErr || sqlite3_malloc_failed ) goto update_cleanup; - db = pParse->db; - assert( pTabList->nSrc==1 ); - - /* Locate the table which we want to update. - */ - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ) goto update_cleanup; - before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, - TK_UPDATE, TK_BEFORE, TK_ROW, pChanges); - after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, - TK_UPDATE, TK_AFTER, TK_ROW, pChanges); - row_triggers_exist = before_triggers || after_triggers; - isView = pTab->pSelect!=0; - if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){ - goto update_cleanup; - } - if( isView ){ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto update_cleanup; - } - } - aXRef = sqliteMallocRaw( sizeof(int) * pTab->nCol ); - if( aXRef==0 ) goto update_cleanup; - for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; - - /* If there are FOR EACH ROW triggers, allocate cursors for the - ** special OLD and NEW tables - */ - if( row_triggers_exist ){ - newIdx = pParse->nTab++; - oldIdx = pParse->nTab++; - } - - /* Allocate a cursors for the main database table and for all indices. - ** The index cursors might not be used, but if they are used they - ** need to occur right after the database cursor. So go ahead and - ** allocate enough space, just in case. - */ - pTabList->a[0].iCursor = iCur = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - pParse->nTab++; - } - - /* Resolve the column names in all the expressions of the - ** of the UPDATE statement. Also find the column index - ** for each column to be updated in the pChanges array. For each - ** column to be updated, make sure we have authorization to change - ** that column. - */ - chngRecno = 0; - for(i=0; i<pChanges->nExpr; i++){ - if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, - pChanges->a[i].pExpr, 0, 0) ){ - goto update_cleanup; - } - for(j=0; j<pTab->nCol; j++){ - if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){ - if( j==pTab->iPKey ){ - chngRecno = 1; - pRecnoExpr = pChanges->a[i].pExpr; - } - aXRef[j] = i; - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pChanges->a[i].zName) ){ - chngRecno = 1; - pRecnoExpr = pChanges->a[i].pExpr; - }else{ - sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); - goto update_cleanup; - } - } -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int rc; - rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - pTab->aCol[j].zName, db->aDb[pTab->iDb].zName); - if( rc==SQLITE_DENY ){ - goto update_cleanup; - }else if( rc==SQLITE_IGNORE ){ - aXRef[j] = -1; - } - } -#endif - } - - /* Allocate memory for the array apIdx[] and fill it with pointers to every - ** index that needs to be updated. Indices only need updating if their - ** key includes one of the columns named in pChanges or if the record - ** number of the original table entry is changing. - */ - for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){ - if( chngRecno ){ - i = 0; - }else { - for(i=0; i<pIdx->nColumn; i++){ - if( aXRef[pIdx->aiColumn[i]]>=0 ) break; - } - } - if( i<pIdx->nColumn ) nIdx++; - } - if( nIdxTotal>0 ){ - apIdx = sqliteMallocRaw( sizeof(Index*) * nIdx + nIdxTotal ); - if( apIdx==0 ) goto update_cleanup; - aIdxUsed = (char*)&apIdx[nIdx]; - } - for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - if( chngRecno ){ - i = 0; - }else{ - for(i=0; i<pIdx->nColumn; i++){ - if( aXRef[pIdx->aiColumn[i]]>=0 ) break; - } - } - if( i<pIdx->nColumn ){ - if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto update_cleanup; - apIdx[nIdx++] = pIdx; - aIdxUsed[j] = 1; - }else{ - aIdxUsed[j] = 0; - } - } - - /* Resolve the column names in all the expressions in the - ** WHERE clause. - */ - if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){ - goto update_cleanup; - } - - /* Start the view context - */ - if( isView ){ - sqlite3AuthContextPush(pParse, &sContext, pTab->zName); - } - - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto update_cleanup; - sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, 1, pTab->iDb); - - /* If we are trying to update a view, construct that view into - ** a temporary table. - */ - if( isView ){ - Select *pView; - pView = sqlite3SelectDup(pTab->pSelect); - sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0); - sqlite3SelectDelete(pView); - } - - /* Begin the database scan - */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); - if( pWInfo==0 ) goto update_cleanup; - - /* Remember the index of every item to be updated. - */ - sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0); - - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - - /* Initialize the count of updated rows - */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ - sqlite3VdbeAddOp(v, OP_Integer, 0, 0); - } - - if( row_triggers_exist ){ - /* Create pseudo-tables for NEW and OLD - */ - sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol); - sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0); - sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol); - - /* The top of the update loop for when there are triggers. - */ - sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0); - addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, 0); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - - /* Open a cursor and make it point to the record that is - ** being updated. - */ - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - if( !isView ){ - sqlite3OpenTableForReading(v, iCur, pTab); - } - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - - /* Generate the OLD table - */ - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - sqlite3VdbeAddOp(v, OP_RowData, iCur, 0); - sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0); - - /* Generate the NEW table - */ - if( chngRecno ){ - sqlite3ExprCode(pParse, pRecnoExpr); - }else{ - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - } - for(i=0; i<pTab->nCol; i++){ /* TODO: Factor out this loop as common code */ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - continue; - } - j = aXRef[i]; - if( j<0 ){ - sqlite3VdbeAddOp(v, OP_Column, iCur, i); - }else{ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr); - } - } - sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); - if( !isView ){ - sqlite3TableAffinityStr(v, pTab); - } - if( pParse->nErr ) goto update_cleanup; - sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0); - if( !isView ){ - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - - /* Fire the BEFORE and INSTEAD OF triggers - */ - if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, - newIdx, oldIdx, onError, addr) ){ - goto update_cleanup; - } - } - - if( !isView ){ - /* - ** Open every index that needs updating. Note that if any - ** index could potentially invoke a REPLACE conflict resolution - ** action, then we need to open all indices because we might need - ** to be deleting some records. - */ - sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum); - sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); - if( onError==OE_Replace ){ - openAll = 1; - }else{ - openAll = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->onError==OE_Replace ){ - openAll = 1; - break; - } - } - } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - if( openAll || aIdxUsed[i] ){ - sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0); - sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, - (char*)&pIdx->keyInfo, P3_KEYINFO); - assert( pParse->nTab>iCur+i+1 ); - } - } - - /* Loop over every record that needs updating. We have to load - ** the old data for each record to be updated because some columns - ** might not change and we will need to copy the old value. - ** Also, the old data is needed to delete the old index entires. - ** So make the cursor point at the old record. - */ - if( !row_triggers_exist ){ - sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0); - addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, 0); - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - } - sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr); - - /* If the record number will change, push the record number as it - ** will be after the update. (The old record number is currently - ** on top of the stack.) - */ - if( chngRecno ){ - sqlite3ExprCode(pParse, pRecnoExpr); - sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); - } - - /* Compute new data for this record. - */ - for(i=0; i<pTab->nCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - continue; - } - j = aXRef[i]; - if( j<0 ){ - sqlite3VdbeAddOp(v, OP_Column, iCur, i); - }else{ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr); - } - } - - /* Do constraint checks - */ - sqlite3GenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, - onError, addr); - - /* Delete the old indices for the current record. - */ - sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, aIdxUsed); - - /* If changing the record number, delete the old record. - */ - if( chngRecno ){ - sqlite3VdbeAddOp(v, OP_Delete, iCur, 0); - } - - /* Create the new index entries and the new record. - */ - sqlite3CompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, -1); - } - - /* Increment the row counter - */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack){ - sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); - } - - /* If there are triggers, close all the cursors after each iteration - ** through the loop. The fire the after triggers. - */ - if( row_triggers_exist ){ - if( !isView ){ - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - if( openAll || aIdxUsed[i] ) - sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0); - } - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - } - if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, - newIdx, oldIdx, onError, addr) ){ - goto update_cleanup; - } - } - - /* Repeat the above with the next record to be updated, until - ** all record selected by the WHERE clause have been updated. - */ - sqlite3VdbeAddOp(v, OP_Goto, 0, addr); - sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp(v, OP_ListReset, 0, 0); - - /* Close all tables if there were no FOR EACH ROW triggers */ - if( !row_triggers_exist ){ - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - if( openAll || aIdxUsed[i] ){ - sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0); - } - } - sqlite3VdbeAddOp(v, OP_Close, iCur, 0); - }else{ - sqlite3VdbeAddOp(v, OP_Close, newIdx, 0); - sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0); - } - - /* - ** Return the number of rows that were changed. - */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ - sqlite3VdbeAddOp(v, OP_Callback, 1, 0); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "rows updated", P3_STATIC); - } - -update_cleanup: - sqlite3AuthContextPop(&sContext); - sqliteFree(apIdx); - sqliteFree(aXRef); - sqlite3SrcListDelete(pTabList); - sqlite3ExprListDelete(pChanges); - sqlite3ExprDelete(pWhere); - return; -} diff --git a/kopete/plugins/statistics/sqlite/utf.c b/kopete/plugins/statistics/sqlite/utf.c deleted file mode 100644 index 58b1a972..00000000 --- a/kopete/plugins/statistics/sqlite/utf.c +++ /dev/null @@ -1,566 +0,0 @@ -/* -** 2004 April 13 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains routines used to translate between UTF-8, -** UTF-16, UTF-16BE, and UTF-16LE. -** -** $Id$ -** -** Notes on UTF-8: -** -** Byte-0 Byte-1 Byte-2 Byte-3 Value -** 0xxxxxxx 00000000 00000000 0xxxxxxx -** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx -** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx -** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** -** -** Notes on UTF-16: (with wwww+1==uuuuu) -** -** Word-0 Word-1 Value -** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx -** -** -** BOM or Byte Order Mark: -** 0xff 0xfe little-endian utf-16 follows -** 0xfe 0xff big-endian utf-16 follows -** -** -** Handling of malformed strings: -** -** SQLite accepts and processes malformed strings without an error wherever -** possible. However this is not possible when converting between UTF-8 and -** UTF-16. -** -** When converting malformed UTF-8 strings to UTF-16, one instance of the -** replacement character U+FFFD for each byte that cannot be interpeted as -** part of a valid unicode character. -** -** When converting malformed UTF-16 strings to UTF-8, one instance of the -** replacement character U+FFFD for each pair of bytes that cannot be -** interpeted as part of a valid unicode character. -** -** This file contains the following public routines: -** -** sqlite3VdbeMemTranslate() - Translate the encoding used by a Mem* string. -** sqlite3VdbeMemHandleBom() - Handle byte-order-marks in UTF16 Mem* strings. -** sqlite3utf16ByteLen() - Calculate byte-length of a void* UTF16 string. -** sqlite3utf8CharLen() - Calculate char-length of a char* UTF8 string. -** sqlite3utf8LikeCompare() - Do a LIKE match given two UTF8 char* strings. -** -*/ -#include <assert.h> -#include "sqliteInt.h" -#include "vdbeInt.h" - -/* -** This table maps from the first byte of a UTF-8 character to the number -** of trailing bytes expected. A value '255' indicates that the table key -** is not a legal first byte for a UTF-8 character. -*/ -static const u8 xtra_utf8_bytes[256] = { -/* 0xxxxxxx */ -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* 10wwwwww */ -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - -/* 110yyyyy */ -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - -/* 1110zzzz */ -2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - -/* 11110yyy */ -3, 3, 3, 3, 3, 3, 3, 3, 255, 255, 255, 255, 255, 255, 255, 255, -}; - -/* -** This table maps from the number of trailing bytes in a UTF-8 character -** to an integer constant that is effectively calculated for each character -** read by a naive implementation of a UTF-8 character reader. The code -** in the READ_UTF8 macro explains things best. -*/ -static const int xtra_utf8_bits[4] = { -0, -12416, /* (0xC0 << 6) + (0x80) */ -925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */ -63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ -}; - -#define READ_UTF8(zIn, c) { \ - int xtra; \ - c = *(zIn)++; \ - xtra = xtra_utf8_bytes[c]; \ - switch( xtra ){ \ - case 255: c = (int)0xFFFD; break; \ - case 3: c = (c<<6) + *(zIn)++; \ - case 2: c = (c<<6) + *(zIn)++; \ - case 1: c = (c<<6) + *(zIn)++; \ - c -= xtra_utf8_bits[xtra]; \ - } \ -} -int sqlite3ReadUtf8(const unsigned char *z){ - int c; - READ_UTF8(z, c); - return c; -} - -#define SKIP_UTF8(zIn) { \ - zIn += (xtra_utf8_bytes[*(u8 *)zIn] + 1); \ -} - -#define WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (c&0xFF); \ - } \ - else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + ((c>>6)&0x1F); \ - *zOut++ = 0x80 + (c & 0x3F); \ - } \ - else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + ((c>>12)&0x0F); \ - *zOut++ = 0x80 + ((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + ((c>>18) & 0x07); \ - *zOut++ = 0x80 + ((c>>12) & 0x3F); \ - *zOut++ = 0x80 + ((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (c & 0x3F); \ - } \ -} - -#define WRITE_UTF16LE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = (c&0x00FF); \ - *zOut++ = ((c>>8)&0x00FF); \ - }else{ \ - *zOut++ = (((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (c&0x00FF); \ - *zOut++ = (0x00DC + ((c>>8)&0x03)); \ - } \ -} - -#define WRITE_UTF16BE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = ((c>>8)&0x00FF); \ - *zOut++ = (c&0x00FF); \ - }else{ \ - *zOut++ = (0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (0x00DC + ((c>>8)&0x03)); \ - *zOut++ = (c&0x00FF); \ - } \ -} - -#define READ_UTF16LE(zIn, c){ \ - c = (*zIn++); \ - c += ((*zIn++)<<8); \ - if( c>=0xD800 && c<=0xE000 ){ \ - int c2 = (*zIn++); \ - c2 += ((*zIn++)<<8); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - -#define READ_UTF16BE(zIn, c){ \ - c = ((*zIn++)<<8); \ - c += (*zIn++); \ - if( c>=0xD800 && c<=0xE000 ){ \ - int c2 = ((*zIn++)<<8); \ - c2 += (*zIn++); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - -#define SKIP_UTF16BE(zIn){ \ - if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn+1)==0x00)) ){ \ - zIn += 4; \ - }else{ \ - zIn += 2; \ - } \ -} -#define SKIP_UTF16LE(zIn){ \ - zIn++; \ - if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn-1)==0x00)) ){ \ - zIn += 3; \ - }else{ \ - zIn += 1; \ - } \ -} - -#define RSKIP_UTF16LE(zIn){ \ - if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn-1)==0x00)) ){ \ - zIn -= 4; \ - }else{ \ - zIn -= 2; \ - } \ -} -#define RSKIP_UTF16BE(zIn){ \ - zIn--; \ - if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn+1)==0x00)) ){ \ - zIn -= 3; \ - }else{ \ - zIn -= 1; \ - } \ -} - -/* -** If the TRANSLATE_TRACE macro is defined, the value of each Mem is -** printed on stderr on the way into and out of sqlite3VdbeMemTranslate(). -*/ -/* #define TRANSLATE_TRACE 1 */ - -/* -** This routine transforms the internal text encoding used by pMem to -** desiredEnc. It is an error if the string is already of the desired -** encoding, or if *pMem does not contain a string value. -*/ -int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - unsigned char zShort[NBFS]; /* Temporary short output buffer */ - int len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ - int c; - - assert( pMem->flags&MEM_Str ); - assert( pMem->enc!=desiredEnc ); - assert( pMem->enc!=0 ); - assert( pMem->n>=0 ); - -#ifdef TRANSLATE_TRACE - { - char zBuf[100]; - sqlite3VdbeMemPrettyPrint(pMem, zBuf, 100); - fprintf(stderr, "INPUT: %s\n", zBuf); - } -#endif - - /* If the translation is between UTF-16 little and big endian, then - ** all that is required is to swap the byte order. This case is handled - ** differently from the others. - */ - if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){ - u8 temp; - int rc; - rc = sqlite3VdbeMemMakeWriteable(pMem); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_NOMEM ); - return SQLITE_NOMEM; - } - zIn = pMem->z; - zTerm = &zIn[pMem->n]; - while( zIn<zTerm ){ - temp = *zIn; - *zIn = *(zIn+1); - zIn++; - *zIn++ = temp; - } - pMem->enc = desiredEnc; - goto translate_out; - } - - /* Set len to the maximum number of bytes required in the output buffer. */ - if( desiredEnc==SQLITE_UTF8 ){ - /* When converting from UTF-16, the maximum growth results from - ** translating a 2-byte character to a 3-byte UTF-8 character (i.e. - ** code-point 0xFFFC). A single byte is required for the output string - ** nul-terminator. - */ - len = (pMem->n/2) * 3 + 1; - }else{ - /* When converting from UTF-8 to UTF-16 the maximum growth is caused - ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 - ** character. Two bytes are required in the output buffer for the - ** nul-terminator. - */ - len = pMem->n * 2 + 2; - } - - /* Set zIn to point at the start of the input buffer and zTerm to point 1 - ** byte past the end. - ** - ** Variable zOut is set to point at the output buffer. This may be space - ** obtained from malloc(), or Mem.zShort, if it large enough and not in - ** use, or the zShort array on the stack (see above). - */ - zIn = pMem->z; - zTerm = &zIn[pMem->n]; - if( len>NBFS ){ - zOut = sqliteMallocRaw(len); - if( !zOut ) return SQLITE_NOMEM; - }else{ - zOut = zShort; - } - z = zOut; - - if( pMem->enc==SQLITE_UTF8 ){ - if( desiredEnc==SQLITE_UTF16LE ){ - /* UTF-8 -> UTF-16 Little-endian */ - while( zIn<zTerm ){ - READ_UTF8(zIn, c); - WRITE_UTF16LE(z, c); - } - }else{ - assert( desiredEnc==SQLITE_UTF16BE ); - /* UTF-8 -> UTF-16 Big-endian */ - while( zIn<zTerm ){ - READ_UTF8(zIn, c); - WRITE_UTF16BE(z, c); - } - } - pMem->n = z - zOut; - *z++ = 0; - }else{ - assert( desiredEnc==SQLITE_UTF8 ); - if( pMem->enc==SQLITE_UTF16LE ){ - /* UTF-16 Little-endian -> UTF-8 */ - while( zIn<zTerm ){ - READ_UTF16LE(zIn, c); - WRITE_UTF8(z, c); - } - }else{ - /* UTF-16 Little-endian -> UTF-8 */ - while( zIn<zTerm ){ - READ_UTF16BE(zIn, c); - WRITE_UTF8(z, c); - } - } - pMem->n = z - zOut; - } - *z = 0; - assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len ); - - sqlite3VdbeMemRelease(pMem); - pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short); - pMem->enc = desiredEnc; - if( zOut==zShort ){ - memcpy(pMem->zShort, zOut, len); - zOut = pMem->zShort; - pMem->flags |= (MEM_Term|MEM_Short); - }else{ - pMem->flags |= (MEM_Term|MEM_Dyn); - } - pMem->z = zOut; - -translate_out: -#ifdef TRANSLATE_TRACE - { - char zBuf[100]; - sqlite3VdbeMemPrettyPrint(pMem, zBuf, 100); - fprintf(stderr, "OUTPUT: %s\n", zBuf); - } -#endif - return SQLITE_OK; -} - -/* -** This routine checks for a byte-order mark at the beginning of the -** UTF-16 string stored in *pMem. If one is present, it is removed and -** the encoding of the Mem adjusted. This routine does not do any -** byte-swapping, it just sets Mem.enc appropriately. -** -** The allocation (static, dynamic etc.) and encoding of the Mem may be -** changed by this function. -*/ -int sqlite3VdbeMemHandleBom(Mem *pMem){ - int rc = SQLITE_OK; - u8 bom = 0; - - if( pMem->n<0 || pMem->n>1 ){ - u8 b1 = *(u8 *)pMem->z; - u8 b2 = *(((u8 *)pMem->z) + 1); - if( b1==0xFE && b2==0xFF ){ - bom = SQLITE_UTF16BE; - } - if( b1==0xFF && b2==0xFE ){ - bom = SQLITE_UTF16LE; - } - } - - if( bom ){ - /* This function is called as soon as a string is stored in a Mem*, - ** from within sqlite3VdbeMemSetStr(). At that point it is not possible - ** for the string to be stored in Mem.zShort, or for it to be stored - ** in dynamic memory with no destructor. - */ - assert( !(pMem->flags&MEM_Short) ); - assert( !(pMem->flags&MEM_Dyn) || pMem->xDel ); - if( pMem->flags & MEM_Dyn ){ - void (*xDel)(void*) = pMem->xDel; - char *z = pMem->z; - pMem->z = 0; - pMem->xDel = 0; - rc = sqlite3VdbeMemSetStr(pMem, &z[2], pMem->n-2, bom, SQLITE_TRANSIENT); - xDel(z); - }else{ - rc = sqlite3VdbeMemSetStr(pMem, &pMem->z[2], pMem->n-2, bom, - SQLITE_TRANSIENT); - } - } - return rc; -} - -/* -** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, -** return the number of unicode characters in pZ up to (but not including) -** the first 0x00 byte. If nByte is not less than zero, return the -** number of unicode characters in the first nByte of pZ (or up to -** the first 0x00, whichever comes first). -*/ -int sqlite3utf8CharLen(const char *z, int nByte){ - int r = 0; - const char *zTerm; - if( nByte>=0 ){ - zTerm = &z[nByte]; - }else{ - zTerm = (const char *)(-1); - } - assert( z<=zTerm ); - while( *z!=0 && z<zTerm ){ - SKIP_UTF8(z); - r++; - } - return r; -} - -/* -** pZ is a UTF-16 encoded unicode string. If nChar is less than zero, -** return the number of bytes up to (but not including), the first pair -** of consecutive 0x00 bytes in pZ. If nChar is not less than zero, -** then return the number of bytes in the first nChar unicode characters -** in pZ (or up until the first pair of 0x00 bytes, whichever comes first). -*/ -int sqlite3utf16ByteLen(const void *zIn, int nChar){ - int c = 1; - char const *z = zIn; - int n = 0; - if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ - while( c && ((nChar<0) || n<nChar) ){ - READ_UTF16BE(z, c); - n++; - } - }else{ - while( c && ((nChar<0) || n<nChar) ){ - READ_UTF16LE(z, c); - n++; - } - } - return (z-(char const *)zIn)-((c==0)?2:0); -} - -/* -** UTF-16 implementation of the substr() -*/ -void sqlite3utf16Substr( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int y, z; - unsigned char const *zStr; - unsigned char const *zStrEnd; - unsigned char const *zStart; - unsigned char const *zEnd; - int i; - - zStr = (unsigned char const *)sqlite3_value_text16(argv[0]); - zStrEnd = &zStr[sqlite3_value_bytes16(argv[0])]; - y = sqlite3_value_int(argv[1]); - z = sqlite3_value_int(argv[2]); - - if( y>0 ){ - y = y-1; - zStart = zStr; - if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){ - for(i=0; i<y && zStart<zStrEnd; i++) SKIP_UTF16BE(zStart); - }else{ - for(i=0; i<y && zStart<zStrEnd; i++) SKIP_UTF16LE(zStart); - } - }else{ - zStart = zStrEnd; - if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){ - for(i=y; i<0 && zStart>zStr; i++) RSKIP_UTF16BE(zStart); - }else{ - for(i=y; i<0 && zStart>zStr; i++) RSKIP_UTF16LE(zStart); - } - for(; i<0; i++) z -= 1; - } - - zEnd = zStart; - if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){ - for(i=0; i<z && zEnd<zStrEnd; i++) SKIP_UTF16BE(zEnd); - }else{ - for(i=0; i<z && zEnd<zStrEnd; i++) SKIP_UTF16LE(zEnd); - } - - sqlite3_result_text16(context, zStart, zEnd-zStart, SQLITE_TRANSIENT); -} - -#if defined(SQLITE_TEST) -/* -** This routine is called from the TCL test function "translate_selftest". -** It checks that the primitives for serializing and deserializing -** characters in each encoding are inverses of each other. -*/ -void sqlite3utfSelfTest(){ - int i; - unsigned char zBuf[20]; - unsigned char *z; - int n; - int c; - - for(i=0; i<0x00110000; i++){ - z = zBuf; - WRITE_UTF8(z, i); - n = z-zBuf; - z = zBuf; - READ_UTF8(z, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<=0xE000 ) continue; - z = zBuf; - WRITE_UTF16LE(z, i); - n = z-zBuf; - z = zBuf; - READ_UTF16LE(z, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<=0xE000 ) continue; - z = zBuf; - WRITE_UTF16BE(z, i); - n = z-zBuf; - z = zBuf; - READ_UTF16BE(z, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } -} -#endif diff --git a/kopete/plugins/statistics/sqlite/util.c b/kopete/plugins/statistics/sqlite/util.c deleted file mode 100644 index 74ec8979..00000000 --- a/kopete/plugins/statistics/sqlite/util.c +++ /dev/null @@ -1,962 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Utility functions used throughout sqlite. -** -** This file contains functions for allocating memory, comparing -** strings, and stuff like that. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include <stdarg.h> -#include <ctype.h> - -#if SQLITE_DEBUG>2 && defined(__GLIBC__) -#include <execinfo.h> -void print_stack_trace(){ - void *bt[30]; - int i; - int n = backtrace(bt, 30); - - sqlite3DebugPrintf("STACK: "); - for(i=0; i<n;i++){ - sqlite3DebugPrintf("%p ", bt[i]); - } - sqlite3DebugPrintf("\n"); -} -#else -#define print_stack_trace() -#endif - -/* -** If malloc() ever fails, this global variable gets set to 1. -** This causes the library to abort and never again function. -*/ -int sqlite3_malloc_failed = 0; - -/* -** If SQLITE_DEBUG is defined, then use versions of malloc() and -** free() that track memory usage and check for buffer overruns. -*/ -#ifdef SQLITE_DEBUG - -/* -** For keeping track of the number of mallocs and frees. This -** is used to check for memory leaks. -*/ -int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */ -int sqlite3_nFree; /* Number of sqliteFree() calls */ -int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */ -#if SQLITE_DEBUG>1 -static int memcnt = 0; -#endif - -/* -** Number of 32-bit guard words -*/ -#define N_GUARD 1 - -/* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. -*/ -void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){ - void *p; - int *pi; - int i, k; - if( sqlite3_iMallocFail>=0 ){ - sqlite3_iMallocFail--; - if( sqlite3_iMallocFail==0 ){ - sqlite3_malloc_failed++; -#if SQLITE_DEBUG>1 - fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n", - n, zFile,line); -#endif - sqlite3_iMallocFail--; - return 0; - } - } - if( n==0 ) return 0; - k = (n+sizeof(int)-1)/sizeof(int); - pi = malloc( (N_GUARD*2+1+k)*sizeof(int)); - if( pi==0 ){ - sqlite3_malloc_failed++; - return 0; - } - sqlite3_nMalloc++; - for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122; - pi[N_GUARD] = n; - for(i=0; i<N_GUARD; i++) pi[k+1+N_GUARD+i] = 0xdead3344; - p = &pi[N_GUARD+1]; - memset(p, bZero==0, n); -#if SQLITE_DEBUG>1 - print_stack_trace(); - fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - return p; -} - -/* -** Check to see if the given pointer was obtained from sqliteMalloc() -** and is able to hold at least N bytes. Raise an exception if this -** is not the case. -** -** This routine is used for testing purposes only. -*/ -void sqlite3CheckMemory(void *p, int N){ - int *pi = p; - int n, i, k; - pi -= N_GUARD+1; - for(i=0; i<N_GUARD; i++){ - assert( pi[i]==0xdead1122 ); - } - n = pi[N_GUARD]; - assert( N>=0 && N<n ); - k = (n+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - assert( pi[k+N_GUARD+1+i]==0xdead3344 ); - } -} - -/* -** Free memory previously obtained from sqliteMalloc() -*/ -void sqlite3Free_(void *p, char *zFile, int line){ - if( p ){ - int *pi, i, k, n; - pi = p; - pi -= N_GUARD+1; - sqlite3_nFree++; - for(i=0; i<N_GUARD; i++){ - if( pi[i]!=0xdead1122 ){ - fprintf(stderr,"Low-end memory corruption at 0x%x\n", (int)p); - return; - } - } - n = pi[N_GUARD]; - k = (n+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - if( pi[k+N_GUARD+1+i]!=0xdead3344 ){ - fprintf(stderr,"High-end memory corruption at 0x%x\n", (int)p); - return; - } - } - memset(pi, 0xff, (k+N_GUARD*2+1)*sizeof(int)); -#if SQLITE_DEBUG>1 - fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - free(pi); - } -} - -/* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). -*/ -void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){ - int *oldPi, *pi, i, k, oldN, oldK; - void *p; - if( oldP==0 ){ - return sqlite3Malloc_(n,1,zFile,line); - } - if( n==0 ){ - sqlite3Free_(oldP,zFile,line); - return 0; - } - oldPi = oldP; - oldPi -= N_GUARD+1; - if( oldPi[0]!=0xdead1122 ){ - fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP); - return 0; - } - oldN = oldPi[N_GUARD]; - oldK = (oldN+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - if( oldPi[oldK+N_GUARD+1+i]!=0xdead3344 ){ - fprintf(stderr,"High-end memory corruption in realloc at 0x%x\n", - (int)oldP); - return 0; - } - } - k = (n + sizeof(int) - 1)/sizeof(int); - pi = malloc( (k+N_GUARD*2+1)*sizeof(int) ); - if( pi==0 ){ - sqlite3_malloc_failed++; - return 0; - } - for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122; - pi[N_GUARD] = n; - for(i=0; i<N_GUARD; i++) pi[k+N_GUARD+1+i] = 0xdead3344; - p = &pi[N_GUARD+1]; - memcpy(p, oldP, n>oldN ? oldN : n); - if( n>oldN ){ - memset(&((char*)p)[oldN], 0x55, n-oldN); - } - memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int)); - free(oldPi); -#if SQLITE_DEBUG>1 - print_stack_trace(); - fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n", - ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line); -#endif - return p; -} - -/* -** Make a copy of a string in memory obtained from sqliteMalloc() -*/ -char *sqlite3StrDup_(const char *z, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line); - if( zNew ) strcpy(zNew, z); - return zNew; -} -char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(n+1, 0, zFile, line); - if( zNew ){ - memcpy(zNew, z, n); - zNew[n] = 0; - } - return zNew; -} - -/* -** A version of sqliteFree that is always a function, not a macro. -*/ -void sqlite3FreeX(void *p){ - sqliteFree(p); -} -#endif /* SQLITE_DEBUG */ - -/* -** The following versions of malloc() and free() are for use in a -** normal build. -*/ -#if !defined(SQLITE_DEBUG) - -/* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. See also sqliteMallocRaw(). -*/ -void *sqlite3Malloc(int n){ - void *p; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3_malloc_failed++; - }else{ - memset(p, 0, n); - } - return p; -} - -/* -** Allocate new memory but do not set it to zero. Return NULL if -** no memory is available. See also sqliteMalloc(). -*/ -void *sqlite3MallocRaw(int n){ - void *p; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3_malloc_failed++; - } - return p; -} - -/* -** Free memory previously obtained from sqliteMalloc() -*/ -void sqlite3FreeX(void *p){ - if( p ){ - free(p); - } -} - -/* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). -*/ -void *sqlite3Realloc(void *p, int n){ - void *p2; - if( p==0 ){ - return sqliteMalloc(n); - } - if( n==0 ){ - sqliteFree(p); - return 0; - } - p2 = realloc(p, n); - if( p2==0 ){ - sqlite3_malloc_failed++; - } - return p2; -} - -/* -** Make a copy of a string in memory obtained from sqliteMalloc() -*/ -char *sqlite3StrDup(const char *z){ - char *zNew; - if( z==0 ) return 0; - zNew = sqliteMallocRaw(strlen(z)+1); - if( zNew ) strcpy(zNew, z); - return zNew; -} -char *sqlite3StrNDup(const char *z, int n){ - char *zNew; - if( z==0 ) return 0; - zNew = sqliteMallocRaw(n+1); - if( zNew ){ - memcpy(zNew, z, n); - zNew[n] = 0; - } - return zNew; -} -#endif /* !defined(SQLITE_DEBUG) */ - -/* -** Create a string from the 2nd and subsequent arguments (up to the -** first NULL argument), store the string in memory obtained from -** sqliteMalloc() and make the pointer indicated by the 1st argument -** point to that string. The 1st argument must either be NULL or -** point to memory obtained from sqliteMalloc(). -*/ -void sqlite3SetString(char **pz, const char *zFirst, ...){ - va_list ap; - int nByte; - const char *z; - char *zResult; - - if( pz==0 ) return; - nByte = strlen(zFirst) + 1; - va_start(ap, zFirst); - while( (z = va_arg(ap, const char*))!=0 ){ - nByte += strlen(z); - } - va_end(ap); - sqliteFree(*pz); - *pz = zResult = sqliteMallocRaw( nByte ); - if( zResult==0 ){ - return; - } - strcpy(zResult, zFirst); - zResult += strlen(zResult); - va_start(ap, zFirst); - while( (z = va_arg(ap, const char*))!=0 ){ - strcpy(zResult, z); - zResult += strlen(zResult); - } - va_end(ap); -#ifdef SQLITE_DEBUG -#if SQLITE_DEBUG>1 - fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz); -#endif -#endif -} - -/* -** Set the most recent error code and error string for the sqlite -** handle "db". The error code is set to "err_code". -** -** If it is not NULL, string zFormat specifies the format of the -** error string in the style of the printf functions: The following -** format characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList -** -** zFormat and any string tokens that follow it are assumed to be -** encoded in UTF-8. -** -** To clear the most recent error for slqite handle "db", sqlite3Error -** should be called with err_code set to SQLITE_OK and zFormat set -** to NULL. -*/ -void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){ - if( db && (db->pErr || (db->pErr = sqlite3ValueNew())) ){ - db->errCode = err_code; - if( zFormat ){ - char *z; - va_list ap; - va_start(ap, zFormat); - z = sqlite3VMPrintf(zFormat, ap); - va_end(ap); - sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, sqlite3FreeX); - }else{ - sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC); - } - } -} - -/* -** Add an error message to pParse->zErrMsg and increment pParse->nErr. -** The following formatting characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList -** -** This function should be used to report any error that occurs whilst -** compiling an SQL statement (i.e. within sqlite3_prepare()). The -** last thing the sqlite3_prepare() function does is copy the error -** stored by this function into the database handle using sqlite3Error(). -** Function sqlite3Error() should be used during statement execution -** (sqlite3_step() etc.). -*/ -void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ - va_list ap; - pParse->nErr++; - sqliteFree(pParse->zErrMsg); - va_start(ap, zFormat); - pParse->zErrMsg = sqlite3VMPrintf(zFormat, ap); - va_end(ap); -} - -/* -** Convert an SQL-style quoted string into a normal string by removing -** the quote characters. The conversion is done in-place. If the -** input does not begin with a quote character, then this routine -** is a no-op. -** -** 2002-Feb-14: This routine is extended to remove MS-Access style -** brackets from around identifers. For example: "[a-b-c]" becomes -** "a-b-c". -*/ -void sqlite3Dequote(char *z){ - int quote; - int i, j; - if( z==0 ) return; - quote = z[0]; - switch( quote ){ - case '\'': break; - case '"': break; - case '[': quote = ']'; break; - default: return; - } - for(i=1, j=0; z[i]; i++){ - if( z[i]==quote ){ - if( z[i+1]==quote ){ - z[j++] = quote; - i++; - }else{ - z[j++] = 0; - break; - } - }else{ - z[j++] = z[i]; - } - } -} - -/* An array to map all upper-case characters into their corresponding -** lower-case character. -*/ -const unsigned char sqlite3UpperToLower[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, - 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, - 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, - 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, - 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, - 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, - 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, - 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, - 252,253,254,255 -}; -#define UpperToLower sqlite3UpperToLower - -/* -** This function computes a hash on the name of a keyword. -** Case is not significant. -*/ -int sqlite3HashNoCase(const char *z, int n){ - int h = 0; - if( n<=0 ) n = strlen(z); - while( n > 0 ){ - h = (h<<3) ^ h ^ UpperToLower[(unsigned char)*z++]; - n--; - } - return h & 0x7fffffff; -} - -/* -** Some systems have stricmp(). Others have strcasecmp(). Because -** there is no consistency, we will define our own. -*/ -int sqlite3StrICmp(const char *zLeft, const char *zRight){ - register unsigned char *a, *b; - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return UpperToLower[*a] - UpperToLower[*b]; -} -int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){ - register unsigned char *a, *b; - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; -} - -/* -** Return TRUE if z is a pure numeric string. Return FALSE if the -** string contains any character which is not part of a number. If -** the string is numeric and contains the '.' character, set *realnum -** to TRUE (otherwise FALSE). -** -** An empty string is considered non-numeric. -*/ -int sqlite3IsNumber(const char *z, int *realnum, u8 enc){ - int incr = (enc==SQLITE_UTF8?1:2); - if( enc==SQLITE_UTF16BE ) z++; - if( *z=='-' || *z=='+' ) z += incr; - if( !isdigit(*(u8*)z) ){ - return 0; - } - z += incr; - if( realnum ) *realnum = 0; - while( isdigit(*(u8*)z) ){ z += incr; } - if( *z=='.' ){ - z += incr; - if( !isdigit(*(u8*)z) ) return 0; - while( isdigit(*(u8*)z) ){ z += incr; } - if( realnum ) *realnum = 1; - } - if( *z=='e' || *z=='E' ){ - z += incr; - if( *z=='+' || *z=='-' ) z += incr; - if( !isdigit(*(u8*)z) ) return 0; - while( isdigit(*(u8*)z) ){ z += incr; } - if( realnum ) *realnum = 1; - } - return *z==0; -} - -/* -** The string z[] is an ascii representation of a real number. -** Convert this string to a double. -** -** This routine assumes that z[] really is a valid number. If it -** is not, the result is undefined. -** -** This routine is used instead of the library atof() function because -** the library atof() might want to use "," as the decimal point instead -** of "." depending on how locale is set. But that would cause problems -** for SQL. So this routine always uses "." regardless of locale. -*/ -double sqlite3AtoF(const char *z, const char **pzEnd){ - int sign = 1; - LONGDOUBLE_TYPE v1 = 0.0; - if( *z=='-' ){ - sign = -1; - z++; - }else if( *z=='+' ){ - z++; - } - while( isdigit(*(u8*)z) ){ - v1 = v1*10.0 + (*z - '0'); - z++; - } - if( *z=='.' ){ - LONGDOUBLE_TYPE divisor = 1.0; - z++; - while( isdigit(*(u8*)z) ){ - v1 = v1*10.0 + (*z - '0'); - divisor *= 10.0; - z++; - } - v1 /= divisor; - } - if( *z=='e' || *z=='E' ){ - int esign = 1; - int eval = 0; - LONGDOUBLE_TYPE scale = 1.0; - z++; - if( *z=='-' ){ - esign = -1; - z++; - }else if( *z=='+' ){ - z++; - } - while( isdigit(*(u8*)z) ){ - eval = eval*10 + *z - '0'; - z++; - } - while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; } - while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; } - while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; } - while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; } - if( esign<0 ){ - v1 /= scale; - }else{ - v1 *= scale; - } - } - if( pzEnd ) *pzEnd = z; - return sign<0 ? -v1 : v1; -} - -/* -** Return TRUE if zNum is a 64-bit signed integer and write -** the value of the integer into *pNum. If zNum is not an integer -** or is an integer that is too large to be expressed with 64 bits, -** then return false. If n>0 and the integer is string is not -** exactly n bytes long, return false. -** -** When this routine was originally written it dealt with only -** 32-bit numbers. At that time, it was much faster than the -** atoi() library routine in RedHat 7.2. -*/ -int sqlite3atoi64(const char *zNum, i64 *pNum){ - i64 v = 0; - int neg; - int i, c; - if( *zNum=='-' ){ - neg = 1; - zNum++; - }else if( *zNum=='+' ){ - neg = 0; - zNum++; - }else{ - neg = 0; - } - for(i=0; (c=zNum[i])>='0' && c<='9'; i++){ - v = v*10 + c - '0'; - } - *pNum = neg ? -v : v; - return c==0 && i>0 && - (i<19 || (i==19 && memcmp(zNum,"9223372036854775807",19)<=0)); -} - -/* -** The string zNum represents an integer. There might be some other -** information following the integer too, but that part is ignored. -** If the integer that the prefix of zNum represents will fit in a -** 32-bit signed integer, return TRUE. Otherwise return FALSE. -** -** This routine returns FALSE for the string -2147483648 even that -** that number will in fact fit in a 32-bit integer. But positive -** 2147483648 will not fit in 32 bits. So it seems safer to return -** false. -*/ -static int sqlite3FitsIn32Bits(const char *zNum){ - int i, c; - if( *zNum=='-' || *zNum=='+' ) zNum++; - for(i=0; (c=zNum[i])>='0' && c<='9'; i++){} - return i<10 || (i==10 && memcmp(zNum,"2147483647",10)<=0); -} - -/* -** If zNum represents an integer that will fit in 32-bits, then set -** *pValue to that integer and return true. Otherwise return false. -*/ -int sqlite3GetInt32(const char *zNum, int *pValue){ - if( sqlite3FitsIn32Bits(zNum) ){ - *pValue = atoi(zNum); - return 1; - } - return 0; -} - -/* -** The string zNum represents an integer. There might be some other -** information following the integer too, but that part is ignored. -** If the integer that the prefix of zNum represents will fit in a -** 64-bit signed integer, return TRUE. Otherwise return FALSE. -** -** This routine returns FALSE for the string -9223372036854775808 even that -** that number will, in theory fit in a 64-bit integer. Positive -** 9223373036854775808 will not fit in 64 bits. So it seems safer to return -** false. -*/ -int sqlite3FitsIn64Bits(const char *zNum){ - int i, c; - if( *zNum=='-' || *zNum=='+' ) zNum++; - for(i=0; (c=zNum[i])>='0' && c<='9'; i++){} - return i<19 || (i==19 && memcmp(zNum,"9223372036854775807",19)<=0); -} - - -/* -** Change the sqlite.magic from SQLITE_MAGIC_OPEN to SQLITE_MAGIC_BUSY. -** Return an error (non-zero) if the magic was not SQLITE_MAGIC_OPEN -** when this routine is called. -** -** This routine is a attempt to detect if two threads use the -** same sqlite* pointer at the same time. There is a race -** condition so it is possible that the error is not detected. -** But usually the problem will be seen. The result will be an -** error which can be used to debug the application that is -** using SQLite incorrectly. -** -** Ticket #202: If db->magic is not a valid open value, take care not -** to modify the db structure at all. It could be that db is a stale -** pointer. In other words, it could be that there has been a prior -** call to sqlite3_close(db) and db has been deallocated. And we do -** not want to write into deallocated memory. -*/ -int sqlite3SafetyOn(sqlite3 *db){ - if( db->magic==SQLITE_MAGIC_OPEN ){ - db->magic = SQLITE_MAGIC_BUSY; - return 0; - }else if( db->magic==SQLITE_MAGIC_BUSY || db->magic==SQLITE_MAGIC_ERROR ){ - db->magic = SQLITE_MAGIC_ERROR; - db->flags |= SQLITE_Interrupt; - } - return 1; -} - -/* -** Change the magic from SQLITE_MAGIC_BUSY to SQLITE_MAGIC_OPEN. -** Return an error (non-zero) if the magic was not SQLITE_MAGIC_BUSY -** when this routine is called. -*/ -int sqlite3SafetyOff(sqlite3 *db){ - if( db->magic==SQLITE_MAGIC_BUSY ){ - db->magic = SQLITE_MAGIC_OPEN; - return 0; - }else if( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ERROR ){ - db->magic = SQLITE_MAGIC_ERROR; - db->flags |= SQLITE_Interrupt; - } - return 1; -} - -/* -** Check to make sure we have a valid db pointer. This test is not -** foolproof but it does provide some measure of protection against -** misuse of the interface such as passing in db pointers that are -** NULL or which have been previously closed. If this routine returns -** TRUE it means that the db pointer is invalid and should not be -** dereferenced for any reason. The calling function should invoke -** SQLITE_MISUSE immediately. -*/ -int sqlite3SafetyCheck(sqlite3 *db){ - int magic; - if( db==0 ) return 1; - magic = db->magic; - if( magic!=SQLITE_MAGIC_CLOSED && - magic!=SQLITE_MAGIC_OPEN && - magic!=SQLITE_MAGIC_BUSY ) return 1; - return 0; -} - -/* -** The variable-length integer encoding is as follows: -** -** KEY: -** A = 0xxxxxxx 7 bits of data and one flag bit -** B = 1xxxxxxx 7 bits of data and one flag bit -** C = xxxxxxxx 8 bits of data -** -** 7 bits - A -** 14 bits - BA -** 21 bits - BBA -** 28 bits - BBBA -** 35 bits - BBBBA -** 42 bits - BBBBBA -** 49 bits - BBBBBBA -** 56 bits - BBBBBBBA -** 64 bits - BBBBBBBBC -*/ - -/* -** Write a 64-bit variable-length integer to memory starting at p[0]. -** The length of data write will be between 1 and 9 bytes. The number -** of bytes written is returned. -** -** A variable-length integer consists of the lower 7 bits of each byte -** for all bytes that have the 8th bit set and one byte with the 8th -** bit clear. Except, if we get to the 9th byte, it stores the full -** 8 bits and is the last byte. -*/ -int sqlite3PutVarint(unsigned char *p, u64 v){ - int i, j, n; - u8 buf[10]; - if( v & 0xff00000000000000 ){ - p[8] = v; - v >>= 8; - for(i=7; i>=0; i--){ - p[i] = (v & 0x7f) | 0x80; - v >>= 7; - } - return 9; - } - n = 0; - do{ - buf[n++] = (v & 0x7f) | 0x80; - v >>= 7; - }while( v!=0 ); - buf[0] &= 0x7f; - assert( n<=9 ); - for(i=0, j=n-1; j>=0; j--, i++){ - p[i] = buf[j]; - } - return n; -} - -/* -** Read a 64-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. -*/ -int sqlite3GetVarint(const unsigned char *p, u64 *v){ - u32 x; - u64 x64; - int n; - unsigned char c; - if( ((c = p[0]) & 0x80)==0 ){ - *v = c; - return 1; - } - x = c & 0x7f; - if( ((c = p[1]) & 0x80)==0 ){ - *v = (x<<7) | c; - return 2; - } - x = (x<<7) | (c&0x7f); - if( ((c = p[2]) & 0x80)==0 ){ - *v = (x<<7) | c; - return 3; - } - x = (x<<7) | (c&0x7f); - if( ((c = p[3]) & 0x80)==0 ){ - *v = (x<<7) | c; - return 4; - } - x64 = (x<<7) | (c&0x7f); - n = 4; - do{ - c = p[n++]; - if( n==9 ){ - x64 = (x64<<8) | c; - break; - } - x64 = (x64<<7) | (c&0x7f); - }while( (c & 0x80)!=0 ); - *v = x64; - return n; -} - -/* -** Read a 32-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. -*/ -int sqlite3GetVarint32(const unsigned char *p, u32 *v){ - u32 x; - int n; - unsigned char c; - if( ((c = p[0]) & 0x80)==0 ){ - *v = c; - return 1; - } - x = c & 0x7f; - if( ((c = p[1]) & 0x80)==0 ){ - *v = (x<<7) | c; - return 2; - } - x = (x<<7) | (c & 0x7f); - n = 2; - do{ - x = (x<<7) | ((c = p[n++])&0x7f); - }while( (c & 0x80)!=0 && n<9 ); - *v = x; - return n; -} - -/* -** Return the number of bytes that will be needed to store the given -** 64-bit integer. -*/ -int sqlite3VarintLen(u64 v){ - int i = 0; - do{ - i++; - v >>= 7; - }while( v!=0 && i<9 ); - return i; -} - -/* -** Translate a single byte of Hex into an integer. -*/ -static int hexToInt(int h){ - if( h>='0' && h<='9' ){ - return h - '0'; - }else if( h>='a' && h<='f' ){ - return h - 'a' + 10; - }else if( h>='A' && h<='F' ){ - return h - 'A' + 10; - }else{ - return 0; - } -} - -/* -** Convert a BLOB literal of the form "x'hhhhhh'" into its binary -** value. Return a pointer to its binary value. Space to hold the -** binary value has been obtained from malloc and must be freed by -** the calling routine. -*/ -void *sqlite3HexToBlob(const char *z){ - char *zBlob; - int i; - int n = strlen(z); - if( n%2 ) return 0; - - zBlob = (char *)sqliteMalloc(n/2); - for(i=0; i<n; i+=2){ - zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]); - } - return zBlob; -} - -#if defined(SQLITE_TEST) -/* -** Convert text generated by the "%p" conversion format back into -** a pointer. -*/ -void *sqlite3TextToPtr(const char *z){ - void *p; - u64 v; - u32 v2; - if( z[0]=='0' && z[1]=='x' ){ - z += 2; - } - v = 0; - while( *z ){ - v = (v<<4) + hexToInt(*z); - z++; - } - if( sizeof(p)==sizeof(v) ){ - p = *(void**)&v; - }else{ - assert( sizeof(p)==sizeof(v2) ); - v2 = (u32)v; - p = *(void**)&v2; - } - return p; -} -#endif diff --git a/kopete/plugins/statistics/sqlite/vacuum.c b/kopete/plugins/statistics/sqlite/vacuum.c deleted file mode 100644 index 371a8557..00000000 --- a/kopete/plugins/statistics/sqlite/vacuum.c +++ /dev/null @@ -1,262 +0,0 @@ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the VACUUM command. -** -** Most of the code in this file may be omitted by defining the -** SQLITE_OMIT_VACUUM macro. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" - -#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM -/* -** Generate a random name of 20 character in length. -*/ -static void randomName(unsigned char *zBuf){ - static const unsigned char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "0123456789"; - int i; - sqlite3Randomness(20, zBuf); - for(i=0; i<20; i++){ - zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ]; - } -} - -/* -** Execute zSql on database db. Return an error code. -*/ -static int execSql(sqlite3 *db, const char *zSql){ - sqlite3_stmt *pStmt; - if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ - return sqlite3_errcode(db); - } - while( SQLITE_ROW==sqlite3_step(pStmt) ); - return sqlite3_finalize(pStmt); -} - -/* -** Execute zSql on database db. The statement returns exactly -** one column. Execute this as SQL on the same database. -*/ -static int execExecSql(sqlite3 *db, const char *zSql){ - sqlite3_stmt *pStmt; - int rc; - - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - rc = execSql(db, sqlite3_column_text(pStmt, 0)); - if( rc!=SQLITE_OK ){ - sqlite3_finalize(pStmt); - return rc; - } - } - - return sqlite3_finalize(pStmt); -} - -#endif - -/* -** The non-standard VACUUM command is used to clean up the database, -** collapse free space, etc. It is modelled after the VACUUM command -** in PostgreSQL. -** -** In version 1.0.x of SQLite, the VACUUM command would call -** gdbm_reorganize() on all the database tables. But beginning -** with 2.0.0, SQLite no longer uses GDBM so this command has -** become a no-op. -*/ -void sqlite3Vacuum(Parse *pParse, Token *pTableName){ - Vdbe *v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0); - } - return; -} - -/* -** This routine implements the OP_Vacuum opcode of the VDBE. -*/ -int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ - int rc = SQLITE_OK; /* Return code from service routines */ -#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM - const char *zFilename; /* full pathname of the database file */ - int nFilename; /* number of characters in zFilename[] */ - char *zTemp = 0; /* a temporary file in same directory as zFilename */ - int i; /* Loop counter */ - Btree *pMain; /* The database being vacuumed */ - Btree *pTemp; - char *zSql = 0; - - if( !db->autoCommit ){ - sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", - (char*)0); - rc = SQLITE_ERROR; - goto end_of_vacuum; - } - - /* Get the full pathname of the database file and create a - ** temporary filename in the same directory as the original file. - */ - pMain = db->aDb[0].pBt; - zFilename = sqlite3BtreeGetFilename(pMain); - assert( zFilename ); - if( zFilename[0]=='\0' ){ - /* The in-memory database. Do nothing. Return directly to avoid causing - ** an error trying to DETACH the vacuum_db (which never got attached) - ** in the exit-handler. - */ - return SQLITE_OK; - } - nFilename = strlen(zFilename); - zTemp = sqliteMalloc( nFilename+100 ); - if( zTemp==0 ){ - rc = SQLITE_NOMEM; - goto end_of_vacuum; - } - strcpy(zTemp, zFilename); - i = 0; - do { - zTemp[nFilename] = '-'; - randomName((unsigned char*)&zTemp[nFilename+1]); - } while( i<10 && sqlite3OsFileExists(zTemp) ); - - /* Attach the temporary database as 'vacuum_db'. The synchronous pragma - ** can be set to 'off' for this file, as it is not recovered if a crash - ** occurs anyway. The integrity of the database is maintained by a - ** (possibly synchronous) transaction opened on the main database before - ** sqlite3BtreeCopyFile() is called. - ** - ** An optimisation would be to use a non-journaled pager. - */ - zSql = sqlite3MPrintf("ATTACH '%q' AS vacuum_db;", zTemp); - if( !zSql ){ - rc = SQLITE_NOMEM; - goto end_of_vacuum; - } - rc = execSql(db, zSql); - sqliteFree(zSql); - zSql = 0; - if( rc!=SQLITE_OK ) goto end_of_vacuum; - assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); - pTemp = db->aDb[db->nDb-1].pBt; - sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), - sqlite3BtreeGetReserve(pMain)); - assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) ); - execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); - - /* Begin a transaction */ - rc = execSql(db, "BEGIN;"); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Query the schema of the main database. Create a mirror schema - ** in the temporary database. - */ - rc = execExecSql(db, - "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " - " FROM sqlite_master WHERE type='table' " - "UNION ALL " - "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000) " - " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' " - "UNION ALL " - "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) " - " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'" - "UNION ALL " - "SELECT 'CREATE VIEW vacuum_db.' || substr(sql,13,100000000) " - " FROM sqlite_master WHERE type='view'" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Loop through the tables in the main database. For each, do - ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy - ** the contents to the temporary database. - */ - rc = execExecSql(db, - "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM ' || quote(name) || ';'" - "FROM sqlite_master " - "WHERE type = 'table';" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Copy the triggers from the main database to the temporary database. - ** This was deferred before in case the triggers interfered with copying - ** the data. It's possible the indices should be deferred until this - ** point also. - */ - rc = execExecSql(db, - "SELECT 'CREATE TRIGGER vacuum_db.' || substr(sql, 16, 1000000) " - "FROM sqlite_master WHERE type='trigger'" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - - /* At this point, unless the main db was completely empty, there is now a - ** transaction open on the vacuum database, but not on the main database. - ** Open a btree level transaction on the main database. This allows a - ** call to sqlite3BtreeCopyFile(). The main database btree level - ** transaction is then committed, so the SQL level never knows it was - ** opened for writing. This way, the SQL transaction used to create the - ** temporary database never needs to be committed. - */ - if( sqlite3BtreeIsInTrans(pTemp) ){ - u32 meta; - - assert( 0==sqlite3BtreeIsInTrans(pMain) ); - rc = sqlite3BtreeBeginTrans(pMain, 1); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta - ** values 2 and 3, the default values of a couple of pragmas. - */ - rc = sqlite3BtreeGetMeta(pMain, 3, &meta); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeGetMeta(pMain, 4, &meta); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - rc = sqlite3BtreeCopyFile(pMain, pTemp); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeCommit(pMain); - } - -end_of_vacuum: - /* Currently there is an SQL level transaction open on the vacuum - ** database. No locks are held on any other files (since the main file - ** was committed at the btree level). So it safe to end the transaction - ** by manually setting the autoCommit flag to true and detaching the - ** vacuum database. The vacuum_db journal file is deleted when the pager - ** is closed by the DETACH. - */ - db->autoCommit = 1; - if( rc==SQLITE_OK ){ - rc = execSql(db, "DETACH vacuum_db;"); - }else{ - execSql(db, "DETACH vacuum_db;"); - } - if( zTemp ){ - sqlite3OsDelete(zTemp); - sqliteFree(zTemp); - } - if( zSql ) sqliteFree( zSql ); - sqlite3ResetInternalSchema(db, 0); -#endif - return rc; -} diff --git a/kopete/plugins/statistics/sqlite/vdbe.c b/kopete/plugins/statistics/sqlite/vdbe.c deleted file mode 100644 index 58f8c731..00000000 --- a/kopete/plugins/statistics/sqlite/vdbe.c +++ /dev/null @@ -1,4450 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** The code in this file implements execution method of the -** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c") -** handles housekeeping details such as creating and deleting -** VDBE instances. This file is solely interested in executing -** the VDBE program. -** -** In the external interface, an "sqlite3_stmt*" is an opaque pointer -** to a VDBE. -** -** The SQL parser generates a program which is then executed by -** the VDBE to do the work of the SQL statement. VDBE programs are -** similar in form to assembly language. The program consists of -** a linear sequence of operations. Each operation has an opcode -** and 3 operands. Operands P1 and P2 are integers. Operand P3 -** is a null-terminated string. The P2 operand must be non-negative. -** Opcodes will typically ignore one or more operands. Many opcodes -** ignore all three operands. -** -** Computation results are stored on a stack. Each entry on the -** stack is either an integer, a null-terminated string, a floating point -** number, or the SQL "NULL" value. An inplicit conversion from one -** type to the other occurs as necessary. -** -** Most of the code in this file is taken up by the sqlite3VdbeExec() -** function which does the work of interpreting a VDBE program. -** But other routines are also provided to help in building up -** a program instruction by instruction. -** -** Various scripts scan this source file in order to generate HTML -** documentation, headers files, or other derived files. The formatting -** of the code in this file is, therefore, important. See other comments -** in this file for details. If in doubt, do not deviate from existing -** commenting and indentation practices when changing or adding code. -** -** $Id$ -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> -#include "vdbeInt.h" - -/* -** The following global variable is incremented every time a cursor -** moves, either by the OP_MoveXX, OP_Next, or OP_Prev opcodes. The test -** procedures use this information to make sure that indices are -** working correctly. This variable has no function other than to -** help verify the correct operation of the library. -*/ -int sqlite3_search_count = 0; - -/* -** When this global variable is positive, it gets decremented once before -** each instruction in the VDBE. When reaches zero, the SQLITE_Interrupt -** of the db.flags field is set in order to simulate and interrupt. -** -** This facility is used for testing purposes only. It does not function -** in an ordinary build. -*/ -int sqlite3_interrupt_count = 0; - -/* -** Release the memory associated with the given stack level. This -** leaves the Mem.flags field in an inconsistent state. -*/ -#define Release(P) if((P)->flags&MEM_Dyn){ sqlite3VdbeMemRelease(P); } - -/* -** Convert the given stack entity into a string if it isn't one -** already. Return non-zero if a malloc() fails. -*/ -#define Stringify(P, enc) \ - if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc)) \ - { goto no_mem; } - -/* -** Convert the given stack entity into a string that has been obtained -** from sqliteMalloc(). This is different from Stringify() above in that -** Stringify() will use the NBFS bytes of static string space if the string -** will fit but this routine always mallocs for space. -** Return non-zero if we run out of memory. -*/ -#define Dynamicify(P,enc) sqlite3VdbeMemDynamicify(P) - - -/* -** An ephemeral string value (signified by the MEM_Ephem flag) contains -** a pointer to a dynamically allocated string where some other entity -** is responsible for deallocating that string. Because the stack entry -** does not control the string, it might be deleted without the stack -** entry knowing it. -** -** This routine converts an ephemeral string into a dynamically allocated -** string that the stack entry itself controls. In other words, it -** converts an MEM_Ephem string into an MEM_Dyn string. -*/ -#define Deephemeralize(P) \ - if( ((P)->flags&MEM_Ephem)!=0 \ - && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} - -/* -** Convert the given stack entity into a integer if it isn't one -** already. -** -** Any prior string or real representation is invalidated. -** NULLs are converted into 0. -*/ -#define Integerify(P) sqlite3VdbeMemIntegerify(P) - -/* -** Convert P so that it has type MEM_Real. -** -** Any prior string or integer representation is invalidated. -** NULLs are converted into 0.0. -*/ -#define Realify(P) sqlite3VdbeMemRealify(P) - -/* -** Argument pMem points at a memory cell that will be passed to a -** user-defined function or returned to the user as the result of a query. -** The second argument, 'db_enc' is the text encoding used by the vdbe for -** stack variables. This routine sets the pMem->enc and pMem->type -** variables used by the sqlite3_value_*() routines. -*/ -#define storeTypeInfo(A,B) _storeTypeInfo(A) -static void _storeTypeInfo(Mem *pMem){ - int flags = pMem->flags; - if( flags & MEM_Null ){ - pMem->type = SQLITE_NULL; - } - else if( flags & MEM_Int ){ - pMem->type = SQLITE_INTEGER; - } - else if( flags & MEM_Real ){ - pMem->type = SQLITE_FLOAT; - } - else if( flags & MEM_Str ){ - pMem->type = SQLITE_TEXT; - }else{ - pMem->type = SQLITE_BLOB; - } -} - -/* -** Insert a new aggregate element and make it the element that -** has focus. -** -** Return 0 on success and 1 if memory is exhausted. -*/ -static int AggInsert(Agg *p, char *zKey, int nKey){ - AggElem *pElem; - int i; - int rc; - pElem = sqliteMalloc( sizeof(AggElem) + nKey + - (p->nMem-1)*sizeof(pElem->aMem[0]) ); - if( pElem==0 ) return SQLITE_NOMEM; - pElem->zKey = (char*)&pElem->aMem[p->nMem]; - memcpy(pElem->zKey, zKey, nKey); - pElem->nKey = nKey; - - if( p->pCsr ){ - rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*)); - if( rc!=SQLITE_OK ){ - sqliteFree(pElem); - return rc; - } - } - - for(i=0; i<p->nMem; i++){ - pElem->aMem[i].flags = MEM_Null; - } - p->pCurrent = pElem; - return 0; -} - -/* -** Pop the stack N times. -*/ -static void popStack(Mem **ppTos, int N){ - Mem *pTos = *ppTos; - while( N>0 ){ - N--; - Release(pTos); - pTos--; - } - *ppTos = pTos; -} - -/* -** The parameters are pointers to the head of two sorted lists -** of Sorter structures. Merge these two lists together and return -** a single sorted list. This routine forms the core of the merge-sort -** algorithm. -** -** In the case of a tie, left sorts in front of right. -*/ -static Sorter *Merge(Sorter *pLeft, Sorter *pRight, KeyInfo *pKeyInfo){ - Sorter sHead; - Sorter *pTail; - pTail = &sHead; - pTail->pNext = 0; - while( pLeft && pRight ){ - int c = sqlite3VdbeRecordCompare(pKeyInfo, pLeft->nKey, pLeft->zKey, - pRight->nKey, pRight->zKey); - if( c<=0 ){ - pTail->pNext = pLeft; - pLeft = pLeft->pNext; - }else{ - pTail->pNext = pRight; - pRight = pRight->pNext; - } - pTail = pTail->pNext; - } - if( pLeft ){ - pTail->pNext = pLeft; - }else if( pRight ){ - pTail->pNext = pRight; - } - return sHead.pNext; -} - -/* -** Allocate cursor number iCur. Return a pointer to it. Return NULL -** if we run out of memory. -*/ -static Cursor *allocateCursor(Vdbe *p, int iCur){ - Cursor *pCx; - assert( iCur<p->nCursor ); - if( p->apCsr[iCur] ){ - sqlite3VdbeFreeCursor(p->apCsr[iCur]); - } - p->apCsr[iCur] = pCx = sqliteMalloc( sizeof(Cursor) ); - return pCx; -} - -/* -** Apply any conversion required by the supplied column affinity to -** memory cell pRec. affinity may be one of: -** -** SQLITE_AFF_NUMERIC -** SQLITE_AFF_TEXT -** SQLITE_AFF_NONE -** SQLITE_AFF_INTEGER -** -*/ -static void applyAffinity(Mem *pRec, char affinity, u8 enc){ - if( affinity==SQLITE_AFF_NONE ){ - /* do nothing */ - }else if( affinity==SQLITE_AFF_TEXT ){ - /* Only attempt the conversion to TEXT if there is an integer or real - ** representation (blob and NULL do not get converted) but no string - ** representation. - */ - if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){ - sqlite3VdbeMemStringify(pRec, enc); - } - pRec->flags &= ~(MEM_Real|MEM_Int); - }else{ - if( 0==(pRec->flags&(MEM_Real|MEM_Int)) ){ - /* pRec does not have a valid integer or real representation. - ** Attempt a conversion if pRec has a string representation and - ** it looks like a number. - */ - int realnum; - sqlite3VdbeMemNulTerminate(pRec); - if( pRec->flags&MEM_Str && sqlite3IsNumber(pRec->z, &realnum, enc) ){ - if( realnum ){ - Realify(pRec); - }else{ - Integerify(pRec); - } - } - } - - if( affinity==SQLITE_AFF_INTEGER ){ - /* For INTEGER affinity, try to convert a real value to an int */ - if( (pRec->flags&MEM_Real) && !(pRec->flags&MEM_Int) ){ - pRec->i = pRec->r; - if( ((double)pRec->i)==pRec->r ){ - pRec->flags |= MEM_Int; - } - } - } - } -} - -#ifndef NDEBUG -/* -** Write a nice string representation of the contents of cell pMem -** into buffer zBuf, length nBuf. -*/ -void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf){ - char *zCsr = zBuf; - int f = pMem->flags; - - static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"}; - - if( f&MEM_Blob ){ - int i; - char c; - if( f & MEM_Dyn ){ - c = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - c = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - c = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - c = 's'; - } - - zCsr += sprintf(zCsr, "%c", c); - zCsr += sprintf(zCsr, "%d[", pMem->n); - for(i=0; i<16 && i<pMem->n; i++){ - zCsr += sprintf(zCsr, "%02X ", ((int)pMem->z[i] & 0xFF)); - } - for(i=0; i<16 && i<pMem->n; i++){ - char z = pMem->z[i]; - if( z<32 || z>126 ) *zCsr++ = '.'; - else *zCsr++ = z; - } - - zCsr += sprintf(zCsr, "]"); - *zCsr = '\0'; - }else if( f & MEM_Str ){ - int j, k; - zBuf[0] = ' '; - if( f & MEM_Dyn ){ - zBuf[1] = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - zBuf[1] = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - zBuf[1] = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - zBuf[1] = 's'; - } - k = 2; - k += sprintf(&zBuf[k], "%d", pMem->n); - zBuf[k++] = '['; - for(j=0; j<15 && j<pMem->n; j++){ - u8 c = pMem->z[j]; - if( c>=0x20 && c<0x7f ){ - zBuf[k++] = c; - }else{ - zBuf[k++] = '.'; - } - } - zBuf[k++] = ']'; - k += sprintf(&zBuf[k], encnames[pMem->enc]); - zBuf[k++] = 0; - } -} -#endif - - -#ifdef VDBE_PROFILE -/* -** The following routine only works on pentium-class processors. -** It uses the RDTSC opcode to read cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -__inline__ unsigned long long int hwtime(void){ - unsigned long long int x; - __asm__("rdtsc\n\t" - "mov %%edx, %%ecx\n\t" - :"=A" (x)); - return x; -} -#endif - -/* -** The CHECK_FOR_INTERRUPT macro defined here looks to see if the -** sqlite3_interrupt() routine has been called. If it has been, then -** processing of the VDBE program is interrupted. -** -** This macro added to every instruction that does a jump in order to -** implement a loop. This test used to be on every single instruction, -** but that meant we more testing that we needed. By only testing the -** flag on jump instructions, we get a (small) speed improvement. -*/ -#define CHECK_FOR_INTERRUPT \ - if( db->flags & SQLITE_Interrupt ) goto abort_due_to_interrupt; - - -/* -** Execute as much of a VDBE program as we can then return. -** -** sqlite3VdbeMakeReady() must be called before this routine in order to -** close the program with a final OP_Halt and to set up the callbacks -** and the error message pointer. -** -** Whenever a row or result data is available, this routine will either -** invoke the result callback (if there is one) or return with -** SQLITE_ROW. -** -** If an attempt is made to open a locked database, then this routine -** will either invoke the busy callback (if there is one) or it will -** return SQLITE_BUSY. -** -** If an error occurs, an error message is written to memory obtained -** from sqliteMalloc() and p->zErrMsg is made to point to that memory. -** The error code is stored in p->rc and this routine returns SQLITE_ERROR. -** -** If the callback ever returns non-zero, then the program exits -** immediately. There will be no error message but the p->rc field is -** set to SQLITE_ABORT and this routine will return SQLITE_ERROR. -** -** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this -** routine to return SQLITE_ERROR. -** -** Other fatal errors return SQLITE_ERROR. -** -** After this routine has finished, sqlite3VdbeFinalize() should be -** used to clean up the mess that was left behind. -*/ -int sqlite3VdbeExec( - Vdbe *p /* The VDBE */ -){ - int pc; /* The program counter */ - Op *pOp; /* Current operation */ - int rc = SQLITE_OK; /* Value to return */ - sqlite3 *db = p->db; /* The database */ - Mem *pTos; /* Top entry in the operand stack */ - char zBuf[100]; /* Space to sprintf() an integer */ -#ifdef VDBE_PROFILE - unsigned long long start; /* CPU clock count at start of opcode */ - int origPc; /* Program counter at start of opcode */ -#endif -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - int nProgressOps = 0; /* Opcodes executed since progress callback. */ -#endif - - if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; - assert( db->magic==SQLITE_MAGIC_BUSY ); - assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY ); - p->rc = SQLITE_OK; - assert( p->explain==0 ); - pTos = p->pTos; - if( sqlite3_malloc_failed ) goto no_mem; - if( p->popStack ){ - popStack(&pTos, p->popStack); - p->popStack = 0; - } - p->resOnStack = 0; - CHECK_FOR_INTERRUPT; - for(pc=p->pc; rc==SQLITE_OK; pc++){ - assert( pc>=0 && pc<p->nOp ); - assert( pTos<=&p->aStack[pc] ); -#ifdef VDBE_PROFILE - origPc = pc; - start = hwtime(); -#endif - pOp = &p->aOp[pc]; - - /* Only allow tracing if NDEBUG is not defined. - */ -#ifndef NDEBUG - if( p->trace ){ - if( pc==0 ){ - printf("VDBE Execution Trace:\n"); - sqlite3VdbePrintSql(p); - } - sqlite3VdbePrintOp(p->trace, pc, pOp); - } -#endif -#ifdef SQLITE_TEST - if( p->trace==0 && pc==0 && sqlite3OsFileExists("vdbe_sqltrace") ){ - sqlite3VdbePrintSql(p); - } -#endif - - - /* Check to see if we need to simulate an interrupt. This only happens - ** if we have a special test build. - */ -#ifdef SQLITE_TEST - if( sqlite3_interrupt_count>0 ){ - sqlite3_interrupt_count--; - if( sqlite3_interrupt_count==0 ){ - sqlite3_interrupt(db); - } - } -#endif - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - /* Call the progress callback if it is configured and the required number - ** of VDBE ops have been executed (either since this invocation of - ** sqlite3VdbeExec() or since last time the progress callback was called). - ** If the progress callback returns non-zero, exit the virtual machine with - ** a return code SQLITE_ABORT. - */ - if( db->xProgress ){ - if( db->nProgressOps==nProgressOps ){ - if( db->xProgress(db->pProgressArg)!=0 ){ - rc = SQLITE_ABORT; - continue; /* skip to the next iteration of the for loop */ - } - nProgressOps = 0; - } - nProgressOps++; - } -#endif - - switch( pOp->opcode ){ - -/***************************************************************************** -** What follows is a massive switch statement where each case implements a -** separate instruction in the virtual machine. If we follow the usual -** indentation conventions, each case should be indented by 6 spaces. But -** that is a lot of wasted space on the left margin. So the code within -** the switch statement will break with convention and be flush-left. Another -** big comment (similar to this one) will mark the point in the code where -** we transition back to normal indentation. -** -** The formatting of each case is important. The makefile for SQLite -** generates two C files "opcodes.h" and "opcodes.c" by scanning this -** file looking for lines that begin with "case OP_". The opcodes.h files -** will be filled with #defines that give unique integer values to each -** opcode and the opcodes.c file is filled with an array of strings where -** each string is the symbolic name for the corresponding opcode. If the -** case statement is followed by a comment of the form "/# same as ... #/" -** that comment is used to determine the particular value of the opcode. -** -** Documentation about VDBE opcodes is generated by scanning this file -** for lines of that contain "Opcode:". That line and all subsequent -** comment lines are used in the generation of the opcode.html documentation -** file. -** -** SUMMARY: -** -** Formatting is important to scripts that scan this file. -** Do not deviate from the formatting style currently in use. -** -*****************************************************************************/ - -/* Opcode: Goto * P2 * -** -** An unconditional jump to address P2. -** The next instruction executed will be -** the one at index P2 from the beginning of -** the program. -*/ -case OP_Goto: { - CHECK_FOR_INTERRUPT; - pc = pOp->p2 - 1; - break; -} - -/* Opcode: Gosub * P2 * -** -** Push the current address plus 1 onto the return address stack -** and then jump to address P2. -** -** The return address stack is of limited depth. If too many -** OP_Gosub operations occur without intervening OP_Returns, then -** the return address stack will fill up and processing will abort -** with a fatal error. -*/ -case OP_Gosub: { - assert( p->returnDepth<sizeof(p->returnStack)/sizeof(p->returnStack[0]) ); - p->returnStack[p->returnDepth++] = pc+1; - pc = pOp->p2 - 1; - break; -} - -/* Opcode: Return * * * -** -** Jump immediately to the next instruction after the last unreturned -** OP_Gosub. If an OP_Return has occurred for all OP_Gosubs, then -** processing aborts with a fatal error. -*/ -case OP_Return: { - assert( p->returnDepth>0 ); - p->returnDepth--; - pc = p->returnStack[p->returnDepth] - 1; - break; -} - -/* Opcode: Halt P1 P2 * -** -** Exit immediately. All open cursors, Lists, Sorts, etc are closed -** automatically. -** -** P1 is the result code returned by sqlite3_exec(), sqlite3_reset(), -** or sqlite3_finalize(). For a normal halt, this should be SQLITE_OK (0). -** For errors, it can be some other value. If P1!=0 then P2 will determine -** whether or not to rollback the current transaction. Do not rollback -** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort, -** then back out all changes that have occurred during this execution of the -** VDBE, but do not rollback the transaction. -** -** There is an implied "Halt 0 0 0" instruction inserted at the very end of -** every program. So a jump past the last instruction of the program -** is the same as executing Halt. -*/ -case OP_Halt: { - p->pTos = pTos; - p->rc = pOp->p1; - p->pc = pc; - p->errorAction = pOp->p2; - if( pOp->p3 ){ - sqlite3SetString(&p->zErrMsg, pOp->p3, (char*)0); - } - rc = sqlite3VdbeHalt(p); - if( rc==SQLITE_BUSY ){ - p->rc = SQLITE_BUSY; - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - } - return p->rc ? SQLITE_ERROR : SQLITE_DONE; -} - -/* Opcode: Integer P1 * P3 -** -** The integer value P1 is pushed onto the stack. If P3 is not zero -** then it is assumed to be a string representation of the same integer. -** If P1 is zero and P3 is not zero, then the value is derived from P3. -*/ -case OP_Integer: { - pTos++; - if( pOp->p3==0 ){ - pTos->flags = MEM_Int; - pTos->i = pOp->p1; - }else{ - pTos->flags = MEM_Str|MEM_Static|MEM_Term; - pTos->z = pOp->p3; - pTos->n = strlen(pTos->z); - pTos->enc = SQLITE_UTF8; - pTos->i = sqlite3VdbeIntValue(pTos); - pTos->flags |= MEM_Int; - } - break; -} - -/* Opcode: Real * * P3 -** -** The string value P3 is converted to a real and pushed on to the stack. -*/ -case OP_Real: { /* same as TK_FLOAT */ - pTos++; - pTos->flags = MEM_Str|MEM_Static|MEM_Term; - pTos->z = pOp->p3; - pTos->n = strlen(pTos->z); - pTos->enc = SQLITE_UTF8; - pTos->r = sqlite3VdbeRealValue(pTos); - pTos->flags |= MEM_Real; - sqlite3VdbeChangeEncoding(pTos, db->enc); - break; -} - -/* Opcode: String8 * * P3 -** -** P3 points to a nul terminated UTF-8 string. This opcode is transformed -** into an OP_String before it is executed for the first time. -*/ -case OP_String8: { /* same as TK_STRING */ - pOp->opcode = OP_String; - - if( db->enc!=SQLITE_UTF8 && pOp->p3 ){ - pTos++; - sqlite3VdbeMemSetStr(pTos, pOp->p3, -1, SQLITE_UTF8, SQLITE_STATIC); - if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pTos, db->enc) ) goto no_mem; - if( SQLITE_OK!=sqlite3VdbeMemDynamicify(pTos) ) goto no_mem; - pTos->flags &= ~(MEM_Dyn); - pTos->flags |= MEM_Static; - if( pOp->p3type==P3_DYNAMIC ){ - sqliteFree(pOp->p3); - } - pOp->p3type = P3_DYNAMIC; - pOp->p3 = pTos->z; - break; - } - /* Otherwise fall through to the next case, OP_String */ -} - -/* Opcode: String * * P3 -** -** The string value P3 is pushed onto the stack. If P3==0 then a -** NULL is pushed onto the stack. P3 is assumed to be a nul terminated -** string encoded with the database native encoding. -*/ -case OP_String: { - pTos++; - if( pOp->p3 ){ - pTos->flags = MEM_Str|MEM_Static|MEM_Term; - pTos->z = pOp->p3; - if( db->enc==SQLITE_UTF8 ){ - pTos->n = strlen(pTos->z); - }else{ - pTos->n = sqlite3utf16ByteLen(pTos->z, -1); - } - pTos->enc = db->enc; - }else{ - pTos->flags = MEM_Null; - } - break; -} - -/* Opcode: HexBlob * * P3 -** -** P3 is an UTF-8 SQL hex encoding of a blob. The blob is pushed onto the -** vdbe stack. -** -** The first time this instruction executes, in transforms itself into a -** 'Blob' opcode with a binary blob as P3. -*/ -case OP_HexBlob: { /* same as TK_BLOB */ - pOp->opcode = OP_Blob; - pOp->p1 = strlen(pOp->p3)/2; - if( pOp->p1 ){ - char *zBlob = sqlite3HexToBlob(pOp->p3); - if( !zBlob ) goto no_mem; - if( pOp->p3type==P3_DYNAMIC ){ - sqliteFree(pOp->p3); - } - pOp->p3 = zBlob; - pOp->p3type = P3_DYNAMIC; - }else{ - if( pOp->p3type==P3_DYNAMIC ){ - sqliteFree(pOp->p3); - } - pOp->p3type = P3_STATIC; - pOp->p3 = ""; - } - - /* Fall through to the next case, OP_Blob. */ -} - -/* Opcode: Blob P1 * P3 -** -** P3 points to a blob of data P1 bytes long. Push this -** value onto the stack. This instruction is not coded directly -** by the compiler. Instead, the compiler layer specifies -** an OP_HexBlob opcode, with the hex string representation of -** the blob as P3. This opcode is transformed to an OP_Blob -** before execution (within the sqlite3_prepare() function). -*/ -case OP_Blob: { - pTos++; - sqlite3VdbeMemSetStr(pTos, pOp->p3, pOp->p1, 0, 0); - break; -} - -/* Opcode: Variable P1 * * -** -** Push the value of variable P1 onto the stack. A variable is -** an unknown in the original SQL string as handed to sqlite3_compile(). -** Any occurance of the '?' character in the original SQL is considered -** a variable. Variables in the SQL string are number from left to -** right beginning with 1. The values of variables are set using the -** sqlite3_bind() API. -*/ -case OP_Variable: { - int j = pOp->p1 - 1; - assert( j>=0 && j<p->nVar ); - - pTos++; - sqlite3VdbeMemShallowCopy(pTos, &p->aVar[j], MEM_Static); - break; -} - -/* Opcode: Pop P1 * * -** -** P1 elements are popped off of the top of stack and discarded. -*/ -case OP_Pop: { - assert( pOp->p1>=0 ); - popStack(&pTos, pOp->p1); - assert( pTos>=&p->aStack[-1] ); - break; -} - -/* Opcode: Dup P1 P2 * -** -** A copy of the P1-th element of the stack -** is made and pushed onto the top of the stack. -** The top of the stack is element 0. So the -** instruction "Dup 0 0 0" will make a copy of the -** top of the stack. -** -** If the content of the P1-th element is a dynamically -** allocated string, then a new copy of that string -** is made if P2==0. If P2!=0, then just a pointer -** to the string is copied. -** -** Also see the Pull instruction. -*/ -case OP_Dup: { - Mem *pFrom = &pTos[-pOp->p1]; - assert( pFrom<=pTos && pFrom>=p->aStack ); - pTos++; - sqlite3VdbeMemShallowCopy(pTos, pFrom, MEM_Ephem); - if( pOp->p2 ){ - Deephemeralize(pTos); - } - break; -} - -/* Opcode: Pull P1 * * -** -** The P1-th element is removed from its current location on -** the stack and pushed back on top of the stack. The -** top of the stack is element 0, so "Pull 0 0 0" is -** a no-op. "Pull 1 0 0" swaps the top two elements of -** the stack. -** -** See also the Dup instruction. -*/ -case OP_Pull: { - Mem *pFrom = &pTos[-pOp->p1]; - int i; - Mem ts; - - ts = *pFrom; - Deephemeralize(pTos); - for(i=0; i<pOp->p1; i++, pFrom++){ - Deephemeralize(&pFrom[1]); - assert( (pFrom->flags & MEM_Ephem)==0 ); - *pFrom = pFrom[1]; - if( pFrom->flags & MEM_Short ){ - assert( pFrom->flags & (MEM_Str|MEM_Blob) ); - assert( pFrom->z==pFrom[1].zShort ); - pFrom->z = pFrom->zShort; - } - } - *pTos = ts; - if( pTos->flags & MEM_Short ){ - assert( pTos->flags & (MEM_Str|MEM_Blob) ); - assert( pTos->z==pTos[-pOp->p1].zShort ); - pTos->z = pTos->zShort; - } - break; -} - -/* Opcode: Push P1 * * -** -** Overwrite the value of the P1-th element down on the -** stack (P1==0 is the top of the stack) with the value -** of the top of the stack. Then pop the top of the stack. -*/ -case OP_Push: { - Mem *pTo = &pTos[-pOp->p1]; - - assert( pTo>=p->aStack ); - sqlite3VdbeMemMove(pTo, pTos); - pTos--; - break; -} - -/* Opcode: Callback P1 * * -** -** Pop P1 values off the stack and form them into an array. Then -** invoke the callback function using the newly formed array as the -** 3rd parameter. -*/ -case OP_Callback: { - int i; - assert( p->nResColumn==pOp->p1 ); - - for(i=0; i<pOp->p1; i++){ - Mem *pVal = &pTos[0-i]; - sqlite3VdbeMemNulTerminate(pVal); - storeTypeInfo(pVal, db->enc); - } - - p->resOnStack = 1; - p->nCallback++; - p->popStack = pOp->p1; - p->pc = pc + 1; - p->pTos = pTos; - return SQLITE_ROW; -} - -/* Opcode: Concat P1 P2 * -** -** Look at the first P1+2 elements of the stack. Append them all -** together with the lowest element first. The original P1+2 elements -** are popped from the stack if P2==0 and retained if P2==1. If -** any element of the stack is NULL, then the result is NULL. -** -** When P1==1, this routine makes a copy of the top stack element -** into memory obtained from sqliteMalloc(). -*/ -case OP_Concat: { /* same as TK_CONCAT */ - char *zNew; - int nByte; - int nField; - int i, j; - Mem *pTerm; - - /* Loop through the stack elements to see how long the result will be. */ - nField = pOp->p1 + 2; - pTerm = &pTos[1-nField]; - nByte = 0; - for(i=0; i<nField; i++, pTerm++){ - assert( pOp->p2==0 || (pTerm->flags&MEM_Str) ); - if( pTerm->flags&MEM_Null ){ - nByte = -1; - break; - } - Stringify(pTerm, db->enc); - nByte += pTerm->n; - } - - if( nByte<0 ){ - /* If nByte is less than zero, then there is a NULL value on the stack. - ** In this case just pop the values off the stack (if required) and - ** push on a NULL. - */ - if( pOp->p2==0 ){ - popStack(&pTos, nField); - } - pTos++; - pTos->flags = MEM_Null; - }else{ - /* Otherwise malloc() space for the result and concatenate all the - ** stack values. - */ - zNew = sqliteMallocRaw( nByte+2 ); - if( zNew==0 ) goto no_mem; - j = 0; - pTerm = &pTos[1-nField]; - for(i=j=0; i<nField; i++, pTerm++){ - int n = pTerm->n; - assert( pTerm->flags & MEM_Str ); - memcpy(&zNew[j], pTerm->z, n); - j += n; - } - zNew[j] = 0; - zNew[j+1] = 0; - assert( j==nByte ); - - if( pOp->p2==0 ){ - popStack(&pTos, nField); - } - pTos++; - pTos->n = j; - pTos->flags = MEM_Str|MEM_Dyn|MEM_Term; - pTos->xDel = 0; - pTos->enc = db->enc; - pTos->z = zNew; - } - break; -} - -/* Opcode: Add * * * -** -** Pop the top two elements from the stack, add them together, -** and push the result back onto the stack. If either element -** is a string then it is converted to a double using the atof() -** function before the addition. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: Multiply * * * -** -** Pop the top two elements from the stack, multiply them together, -** and push the result back onto the stack. If either element -** is a string then it is converted to a double using the atof() -** function before the multiplication. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: Subtract * * * -** -** Pop the top two elements from the stack, subtract the -** first (what was on top of the stack) from the second (the -** next on stack) -** and push the result back onto the stack. If either element -** is a string then it is converted to a double using the atof() -** function before the subtraction. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: Divide * * * -** -** Pop the top two elements from the stack, divide the -** first (what was on top of the stack) from the second (the -** next on stack) -** and push the result back onto the stack. If either element -** is a string then it is converted to a double using the atof() -** function before the division. Division by zero returns NULL. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: Remainder * * * -** -** Pop the top two elements from the stack, divide the -** first (what was on top of the stack) from the second (the -** next on stack) -** and push the remainder after division onto the stack. If either element -** is a string then it is converted to a double using the atof() -** function before the division. Division by zero returns NULL. -** If either operand is NULL, the result is NULL. -*/ -case OP_Add: /* same as TK_PLUS */ -case OP_Subtract: /* same as TK_MINUS */ -case OP_Multiply: /* same as TK_STAR */ -case OP_Divide: /* same as TK_SLASH */ -case OP_Remainder: { /* same as TK_REM */ - Mem *pNos = &pTos[-1]; - assert( pNos>=p->aStack ); - if( ((pTos->flags | pNos->flags) & MEM_Null)!=0 ){ - Release(pTos); - pTos--; - Release(pTos); - pTos->flags = MEM_Null; - }else if( (pTos->flags & pNos->flags & MEM_Int)==MEM_Int ){ - i64 a, b; - a = pTos->i; - b = pNos->i; - switch( pOp->opcode ){ - case OP_Add: b += a; break; - case OP_Subtract: b -= a; break; - case OP_Multiply: b *= a; break; - case OP_Divide: { - if( a==0 ) goto divide_by_zero; - b /= a; - break; - } - default: { - if( a==0 ) goto divide_by_zero; - b %= a; - break; - } - } - Release(pTos); - pTos--; - Release(pTos); - pTos->i = b; - pTos->flags = MEM_Int; - }else{ - double a, b; - a = sqlite3VdbeRealValue(pTos); - b = sqlite3VdbeRealValue(pNos); - switch( pOp->opcode ){ - case OP_Add: b += a; break; - case OP_Subtract: b -= a; break; - case OP_Multiply: b *= a; break; - case OP_Divide: { - if( a==0.0 ) goto divide_by_zero; - b /= a; - break; - } - default: { - int ia = (int)a; - int ib = (int)b; - if( ia==0.0 ) goto divide_by_zero; - b = ib % ia; - break; - } - } - Release(pTos); - pTos--; - Release(pTos); - pTos->r = b; - pTos->flags = MEM_Real; - } - break; - -divide_by_zero: - Release(pTos); - pTos--; - Release(pTos); - pTos->flags = MEM_Null; - break; -} - -/* Opcode: CollSeq * * P3 -** -** P3 is a pointer to a CollSeq struct. If the next call to a user function -** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will -** be returned. This is used by the built-in min(), max() and nullif() -** built-in functions. -** -** The interface used by the implementation of the aforementioned functions -** to retrieve the collation sequence set by this opcode is not available -** publicly, only to user functions defined in func.c. -*/ -case OP_CollSeq: { - assert( pOp->p3type==P3_COLLSEQ ); - break; -} - -/* Opcode: Function P1 P2 P3 -** -** Invoke a user function (P3 is a pointer to a Function structure that -** defines the function) with P1 arguments taken from the stack. Pop all -** arguments from the stack and push back the result. -** -** P2 is a 32-bit bitmask indicating whether or not each argument to the -** function was determined to be constant at compile time. If the first -** argument was constant then bit 0 of P2 is set. This is used to determine -** whether meta data associated with a user function argument using the -** sqlite3_set_auxdata() API may be safely retained until the next -** invocation of this opcode. -** -** See also: AggFunc -*/ -case OP_Function: { - int i; - Mem *pArg; - sqlite3_context ctx; - sqlite3_value **apVal; - int n = pOp->p1; - - n = pOp->p1; - apVal = p->apArg; - assert( apVal || n==0 ); - - pArg = &pTos[1-n]; - for(i=0; i<n; i++, pArg++){ - apVal[i] = pArg; - storeTypeInfo(pArg, db->enc); - } - - assert( pOp->p3type==P3_FUNCDEF || pOp->p3type==P3_VDBEFUNC ); - if( pOp->p3type==P3_FUNCDEF ){ - ctx.pFunc = (FuncDef*)pOp->p3; - ctx.pVdbeFunc = 0; - }else{ - ctx.pVdbeFunc = (VdbeFunc*)pOp->p3; - ctx.pFunc = ctx.pVdbeFunc->pFunc; - } - - ctx.s.flags = MEM_Null; - ctx.s.z = 0; - ctx.s.xDel = 0; - ctx.isError = 0; - ctx.isStep = 0; - if( ctx.pFunc->needCollSeq ){ - assert( pOp>p->aOp ); - assert( pOp[-1].p3type==P3_COLLSEQ ); - assert( pOp[-1].opcode==OP_CollSeq ); - ctx.pColl = (CollSeq *)pOp[-1].p3; - } - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - (*ctx.pFunc->xFunc)(&ctx, n, apVal); - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; - if( sqlite3_malloc_failed ) goto no_mem; - popStack(&pTos, n); - - /* If any auxilary data functions have been called by this user function, - ** immediately call the destructor for any non-static values. - */ - if( ctx.pVdbeFunc ){ - sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p2); - pOp->p3 = (char *)ctx.pVdbeFunc; - pOp->p3type = P3_VDBEFUNC; - } - - /* Copy the result of the function to the top of the stack */ - sqlite3VdbeChangeEncoding(&ctx.s, db->enc); - pTos++; - pTos->flags = 0; - sqlite3VdbeMemMove(pTos, &ctx.s); - - /* If the function returned an error, throw an exception */ - if( ctx.isError ){ - if( !(pTos->flags&MEM_Str) ){ - sqlite3SetString(&p->zErrMsg, "user function error", (char*)0); - }else{ - sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pTos), (char*)0); - sqlite3VdbeChangeEncoding(pTos, db->enc); - } - rc = SQLITE_ERROR; - } - break; -} - -/* Opcode: BitAnd * * * -** -** Pop the top two elements from the stack. Convert both elements -** to integers. Push back onto the stack the bit-wise AND of the -** two elements. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: BitOr * * * -** -** Pop the top two elements from the stack. Convert both elements -** to integers. Push back onto the stack the bit-wise OR of the -** two elements. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: ShiftLeft * * * -** -** Pop the top two elements from the stack. Convert both elements -** to integers. Push back onto the stack the second element shifted -** left by N bits where N is the top element on the stack. -** If either operand is NULL, the result is NULL. -*/ -/* Opcode: ShiftRight * * * -** -** Pop the top two elements from the stack. Convert both elements -** to integers. Push back onto the stack the second element shifted -** right by N bits where N is the top element on the stack. -** If either operand is NULL, the result is NULL. -*/ -case OP_BitAnd: /* same as TK_BITAND */ -case OP_BitOr: /* same as TK_BITOR */ -case OP_ShiftLeft: /* same as TK_LSHIFT */ -case OP_ShiftRight: { /* same as TK_RSHIFT */ - Mem *pNos = &pTos[-1]; - int a, b; - - assert( pNos>=p->aStack ); - if( (pTos->flags | pNos->flags) & MEM_Null ){ - popStack(&pTos, 2); - pTos++; - pTos->flags = MEM_Null; - break; - } - a = sqlite3VdbeIntValue(pNos); - b = sqlite3VdbeIntValue(pTos); - switch( pOp->opcode ){ - case OP_BitAnd: a &= b; break; - case OP_BitOr: a |= b; break; - case OP_ShiftLeft: a <<= b; break; - case OP_ShiftRight: a >>= b; break; - default: /* CANT HAPPEN */ break; - } - Release(pTos); - pTos--; - Release(pTos); - pTos->i = a; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: AddImm P1 * * -** -** Add the value P1 to whatever is on top of the stack. The result -** is always an integer. -** -** To force the top of the stack to be an integer, just add 0. -*/ -case OP_AddImm: { - assert( pTos>=p->aStack ); - Integerify(pTos); - pTos->i += pOp->p1; - break; -} - -/* Opcode: ForceInt P1 P2 * -** -** Convert the top of the stack into an integer. If the current top of -** the stack is not numeric (meaning that is is a NULL or a string that -** does not look like an integer or floating point number) then pop the -** stack and jump to P2. If the top of the stack is numeric then -** convert it into the least integer that is greater than or equal to its -** current value if P1==0, or to the least integer that is strictly -** greater than its current value if P1==1. -*/ -case OP_ForceInt: { - int v; - assert( pTos>=p->aStack ); - applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc); - if( (pTos->flags & (MEM_Int|MEM_Real))==0 ){ - Release(pTos); - pTos--; - pc = pOp->p2 - 1; - break; - } - if( pTos->flags & MEM_Int ){ - v = pTos->i + (pOp->p1!=0); - }else{ - Realify(pTos); - v = (int)pTos->r; - if( pTos->r>(double)v ) v++; - if( pOp->p1 && pTos->r==(double)v ) v++; - } - Release(pTos); - pTos->i = v; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: MustBeInt P1 P2 * -** -** Force the top of the stack to be an integer. If the top of the -** stack is not an integer and cannot be converted into an integer -** with out data loss, then jump immediately to P2, or if P2==0 -** raise an SQLITE_MISMATCH exception. -** -** If the top of the stack is not an integer and P2 is not zero and -** P1 is 1, then the stack is popped. In all other cases, the depth -** of the stack is unchanged. -*/ -case OP_MustBeInt: { - assert( pTos>=p->aStack ); - applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc); - if( (pTos->flags & MEM_Int)==0 ){ - if( pOp->p2==0 ){ - rc = SQLITE_MISMATCH; - goto abort_due_to_error; - }else{ - if( pOp->p1 ) popStack(&pTos, 1); - pc = pOp->p2 - 1; - } - }else{ - Release(pTos); - pTos->flags = MEM_Int; - } - break; -} - -/* Opcode: Eq P1 P2 P3 -** -** Pop the top two elements from the stack. If they are equal, then -** jump to instruction P2. Otherwise, continue to the next instruction. -** -** The least significant byte of P1 may be either 0x00 or 0x01. If either -** operand is NULL (and thus if the result is unknown) then take the jump -** only if the least significant byte of P1 is 0x01. -** -** The second least significant byte of P1 must be an affinity character - -** 'n', 't', 'i' or 'o' - or 0x00. An attempt is made to coerce both values -** according to the affinity before the comparison is made. If the byte is -** 0x00, then numeric affinity is used. -** -** Once any conversions have taken place, and neither value is NULL, -** the values are compared. If both values are blobs, or both are text, -** then memcmp() is used to determine the results of the comparison. If -** both values are numeric, then a numeric comparison is used. If the -** two values are of different types, then they are inequal. -** -** If P2 is zero, do not jump. Instead, push an integer 1 onto the -** stack if the jump would have been taken, or a 0 if not. Push a -** NULL if either operand was NULL. -** -** If P3 is not NULL it is a pointer to a collating sequence (a CollSeq -** structure) that defines how to compare text. -*/ -/* Opcode: Ne P1 P2 P3 -** -** This works just like the Eq opcode except that the jump is taken if -** the operands from the stack are not equal. See the Eq opcode for -** additional information. -*/ -/* Opcode: Lt P1 P2 P3 -** -** This works just like the Eq opcode except that the jump is taken if -** the 2nd element down on the stack is less than the top of the stack. -** See the Eq opcode for additional information. -*/ -/* Opcode: Le P1 P2 P3 -** -** This works just like the Eq opcode except that the jump is taken if -** the 2nd element down on the stack is less than or equal to the -** top of the stack. See the Eq opcode for additional information. -*/ -/* Opcode: Gt P1 P2 P3 -** -** This works just like the Eq opcode except that the jump is taken if -** the 2nd element down on the stack is greater than the top of the stack. -** See the Eq opcode for additional information. -*/ -/* Opcode: Ge P1 P2 P3 -** -** This works just like the Eq opcode except that the jump is taken if -** the 2nd element down on the stack is greater than or equal to the -** top of the stack. See the Eq opcode for additional information. -*/ -case OP_Eq: /* same as TK_EQ */ -case OP_Ne: /* same as TK_NE */ -case OP_Lt: /* same as TK_LT */ -case OP_Le: /* same as TK_LE */ -case OP_Gt: /* same as TK_GT */ -case OP_Ge: { /* same as TK_GE */ - Mem *pNos; - int flags; - int res; - char affinity; - - pNos = &pTos[-1]; - flags = pTos->flags|pNos->flags; - - /* If either value is a NULL P2 is not zero, take the jump if the least - ** significant byte of P1 is true. If P2 is zero, then push a NULL onto - ** the stack. - */ - if( flags&MEM_Null ){ - popStack(&pTos, 2); - if( pOp->p2 ){ - if( (pOp->p1&0xFF) ) pc = pOp->p2-1; - }else{ - pTos++; - pTos->flags = MEM_Null; - } - break; - } - - affinity = (pOp->p1>>8)&0xFF; - if( affinity ){ - applyAffinity(pNos, affinity, db->enc); - applyAffinity(pTos, affinity, db->enc); - } - - assert( pOp->p3type==P3_COLLSEQ || pOp->p3==0 ); - res = sqlite3MemCompare(pNos, pTos, (CollSeq*)pOp->p3); - switch( pOp->opcode ){ - case OP_Eq: res = res==0; break; - case OP_Ne: res = res!=0; break; - case OP_Lt: res = res<0; break; - case OP_Le: res = res<=0; break; - case OP_Gt: res = res>0; break; - default: res = res>=0; break; - } - - popStack(&pTos, 2); - if( pOp->p2 ){ - if( res ){ - pc = pOp->p2-1; - } - }else{ - pTos++; - pTos->flags = MEM_Int; - pTos->i = res; - } - break; -} - -/* Opcode: And * * * -** -** Pop two values off the stack. Take the logical AND of the -** two values and push the resulting boolean value back onto the -** stack. -*/ -/* Opcode: Or * * * -** -** Pop two values off the stack. Take the logical OR of the -** two values and push the resulting boolean value back onto the -** stack. -*/ -case OP_And: /* same as TK_AND */ -case OP_Or: { /* same as TK_OR */ - Mem *pNos = &pTos[-1]; - int v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ - - assert( pNos>=p->aStack ); - if( pTos->flags & MEM_Null ){ - v1 = 2; - }else{ - Integerify(pTos); - v1 = pTos->i==0; - } - if( pNos->flags & MEM_Null ){ - v2 = 2; - }else{ - Integerify(pNos); - v2 = pNos->i==0; - } - if( pOp->opcode==OP_And ){ - static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - v1 = and_logic[v1*3+v2]; - }else{ - static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - v1 = or_logic[v1*3+v2]; - } - popStack(&pTos, 2); - pTos++; - if( v1==2 ){ - pTos->flags = MEM_Null; - }else{ - pTos->i = v1==0; - pTos->flags = MEM_Int; - } - break; -} - -/* Opcode: Negative * * * -** -** Treat the top of the stack as a numeric quantity. Replace it -** with its additive inverse. If the top of the stack is NULL -** its value is unchanged. -*/ -/* Opcode: AbsValue * * * -** -** Treat the top of the stack as a numeric quantity. Replace it -** with its absolute value. If the top of the stack is NULL -** its value is unchanged. -*/ -case OP_Negative: /* same as TK_UMINUS */ -case OP_AbsValue: { - assert( pTos>=p->aStack ); - if( pTos->flags & MEM_Real ){ - Release(pTos); - if( pOp->opcode==OP_Negative || pTos->r<0.0 ){ - pTos->r = -pTos->r; - } - pTos->flags = MEM_Real; - }else if( pTos->flags & MEM_Int ){ - Release(pTos); - if( pOp->opcode==OP_Negative || pTos->i<0 ){ - pTos->i = -pTos->i; - } - pTos->flags = MEM_Int; - }else if( pTos->flags & MEM_Null ){ - /* Do nothing */ - }else{ - Realify(pTos); - if( pOp->opcode==OP_Negative || pTos->r<0.0 ){ - pTos->r = -pTos->r; - } - pTos->flags = MEM_Real; - } - break; -} - -/* Opcode: Not * * * -** -** Interpret the top of the stack as a boolean value. Replace it -** with its complement. If the top of the stack is NULL its value -** is unchanged. -*/ -case OP_Not: { /* same as TK_NOT */ - assert( pTos>=p->aStack ); - if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */ - Integerify(pTos); - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos->i = !pTos->i; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: BitNot * * * -** -** Interpret the top of the stack as an value. Replace it -** with its ones-complement. If the top of the stack is NULL its -** value is unchanged. -*/ -case OP_BitNot: { /* same as TK_BITNOT */ - assert( pTos>=p->aStack ); - if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */ - Integerify(pTos); - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos->i = ~pTos->i; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: Noop * * * -** -** Do nothing. This instruction is often useful as a jump -** destination. -*/ -case OP_Noop: { - break; -} - -/* Opcode: If P1 P2 * -** -** Pop a single boolean from the stack. If the boolean popped is -** true, then jump to p2. Otherwise continue to the next instruction. -** An integer is false if zero and true otherwise. A string is -** false if it has zero length and true otherwise. -** -** If the value popped of the stack is NULL, then take the jump if P1 -** is true and fall through if P1 is false. -*/ -/* Opcode: IfNot P1 P2 * -** -** Pop a single boolean from the stack. If the boolean popped is -** false, then jump to p2. Otherwise continue to the next instruction. -** An integer is false if zero and true otherwise. A string is -** false if it has zero length and true otherwise. -** -** If the value popped of the stack is NULL, then take the jump if P1 -** is true and fall through if P1 is false. -*/ -case OP_If: -case OP_IfNot: { - int c; - assert( pTos>=p->aStack ); - if( pTos->flags & MEM_Null ){ - c = pOp->p1; - }else{ - c = sqlite3VdbeIntValue(pTos); - if( pOp->opcode==OP_IfNot ) c = !c; - } - Release(pTos); - pTos--; - if( c ) pc = pOp->p2-1; - break; -} - -/* Opcode: IsNull P1 P2 * -** -** If any of the top abs(P1) values on the stack are NULL, then jump -** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack -** unchanged. -*/ -case OP_IsNull: { /* same as TK_ISNULL */ - int i, cnt; - Mem *pTerm; - cnt = pOp->p1; - if( cnt<0 ) cnt = -cnt; - pTerm = &pTos[1-cnt]; - assert( pTerm>=p->aStack ); - for(i=0; i<cnt; i++, pTerm++){ - if( pTerm->flags & MEM_Null ){ - pc = pOp->p2-1; - break; - } - } - if( pOp->p1>0 ) popStack(&pTos, cnt); - break; -} - -/* Opcode: NotNull P1 P2 * -** -** Jump to P2 if the top P1 values on the stack are all not NULL. Pop the -** stack if P1 times if P1 is greater than zero. If P1 is less than -** zero then leave the stack unchanged. -*/ -case OP_NotNull: { /* same as TK_NOTNULL */ - int i, cnt; - cnt = pOp->p1; - if( cnt<0 ) cnt = -cnt; - assert( &pTos[1-cnt] >= p->aStack ); - for(i=0; i<cnt && (pTos[1+i-cnt].flags & MEM_Null)==0; i++){} - if( i>=cnt ) pc = pOp->p2-1; - if( pOp->p1>0 ) popStack(&pTos, cnt); - break; -} - -/* Opcode: SetNumColumns P1 P2 * -** -** Before the OP_Column opcode can be executed on a cursor, this -** opcode must be called to set the number of fields in the table. -** -** This opcode sets the number of columns for cursor P1 to P2. -*/ -case OP_SetNumColumns: { - assert( (pOp->p1)<p->nCursor ); - assert( p->apCsr[pOp->p1]!=0 ); - p->apCsr[pOp->p1]->nField = pOp->p2; - break; -} - -/* Opcode: IdxColumn P1 * * -** -** P1 is a cursor opened on an index. Push the first field from the -** current index key onto the stack. -*/ -/* Opcode: Column P1 P2 * -** -** Interpret the data that cursor P1 points to as a structure built using -** the MakeRecord instruction. (See the MakeRecord opcode for additional -** information about the format of the data.) Push onto the stack the value -** of the P2-th column contained in the data. -** -** If the KeyAsData opcode has previously executed on this cursor, then the -** field might be extracted from the key rather than the data. -** -** If P1 is negative, then the record is stored on the stack rather than in -** a table. For P1==-1, the top of the stack is used. For P1==-2, the -** next on the stack is used. And so forth. The value pushed is always -** just a pointer into the record which is stored further down on the -** stack. The column value is not copied. The number of columns in the -** record is stored on the stack just above the record itself. -*/ -case OP_IdxColumn: -case OP_Column: { - u32 payloadSize; /* Number of bytes in the record */ - int p1 = pOp->p1; /* P1 value of the opcode */ - int p2 = pOp->p2; /* column number to retrieve */ - Cursor *pC = 0; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aType; /* aType[i] holds the numeric type of the i-th column */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - u32 nField; /* number of fields in the record */ - u32 szHdr; /* Number of bytes in the record header */ - int len; /* The length of the serialized data for the column */ - int offset = 0; /* Offset into the data */ - int idx; /* Index into the header */ - int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ - Mem sMem; /* For storing the record being decoded */ - - sMem.flags = 0; - assert( p1<p->nCursor ); - pTos++; - pTos->flags = MEM_Null; - - /* This block sets the variable payloadSize to be the total number of - ** bytes in the record. - ** - ** zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables and - ** when we are decoded a record from the stack. If the record is stored - ** in a cursor, the complete record text might be available in the - ** pC->aRow cache. Or it might not be. If the data is unavailable, - ** zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the Cursor.nField element. For - ** records on the stack, the next entry down on the stack is an integer - ** which is the number of records. - */ - assert( p1<0 || p->apCsr[p1]!=0 ); - if( p1<0 ){ - /* Take the record off of the stack */ - Mem *pRec = &pTos[p1]; - Mem *pCnt = &pRec[-1]; - assert( pRec>=p->aStack ); - assert( pRec->flags & MEM_Blob ); - payloadSize = pRec->n; - zRec = pRec->z; - assert( pCnt>=p->aStack ); - assert( pCnt->flags & MEM_Int ); - nField = pCnt->i; - pCrsr = 0; - }else if( (pC = p->apCsr[p1])->pCursor!=0 ){ - /* The record is stored in a B-Tree */ - sqlite3VdbeCursorMoveto(pC); - zRec = 0; - pCrsr = pC->pCursor; - if( pC->nullRow ){ - payloadSize = 0; - }else if( pC->cacheValid ){ - payloadSize = pC->payloadSize; - zRec = pC->aRow; - }else if( pC->keyAsData ){ - i64 payloadSize64; - sqlite3BtreeKeySize(pCrsr, &payloadSize64); - payloadSize = payloadSize64; - }else{ - sqlite3BtreeDataSize(pCrsr, &payloadSize); - } - nField = pC->nField; - }else if( pC->pseudoTable ){ - /* The record is the sole entry of a pseudo-table */ - payloadSize = pC->nData; - zRec = pC->pData; - pC->cacheValid = 0; - assert( payloadSize==0 || zRec!=0 ); - nField = pC->nField; - pCrsr = 0; - }else{ - zRec = 0; - payloadSize = 0; - pCrsr = 0; - nField = 0; - } - - /* If payloadSize is 0, then just push a NULL onto the stack. */ - if( payloadSize==0 ){ - pTos->flags = MEM_Null; - break; - } - - assert( p2<nField ); - - /* Read and parse the table header. Store the results of the parse - ** into the record header cache fields of the cursor. - */ - if( pC && pC->cacheValid ){ - aType = pC->aType; - aOffset = pC->aOffset; - }else{ - int avail; /* Number of bytes of available data */ - if( pC && pC->aType ){ - aType = pC->aType; - }else{ - aType = sqliteMallocRaw( 2*nField*sizeof(aType) ); - } - aOffset = &aType[nField]; - if( aType==0 ){ - goto no_mem; - } - - /* Figure out how many bytes are in the header */ - if( zRec ){ - zData = zRec; - }else{ - if( pC->keyAsData ){ - zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail); - }else{ - zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail); - } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - if( avail>=payloadSize ){ - zRec = pC->aRow = zData; - }else{ - pC->aRow = 0; - } - } - idx = sqlite3GetVarint32(zData, &szHdr); - - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to - ** acquire the complete header text. - */ - if( !zRec && avail<szHdr ){ - rc = sqlite3VdbeMemFromBtree(pCrsr, 0, szHdr, pC->keyAsData, &sMem); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - zData = sMem.z; - } - - /* Scan the header and use it to fill in the aType[] and aOffset[] - ** arrays. aType[i] will contain the type integer for the i-th - ** column and aOffset[i] will contain the offset from the beginning - ** of the record to the start of the data for the i-th column - */ - offset = szHdr; - i = 0; - while( idx<szHdr && i<nField && offset<=payloadSize ){ - aOffset[i] = offset; - idx += sqlite3GetVarint32(&zData[idx], &aType[i]); - offset += sqlite3VdbeSerialTypeLen(aType[i]); - i++; - } - Release(&sMem); - sMem.flags = MEM_Null; - - /* The header should end at the start of data and the data should - ** end at last byte of the record. If this is not the case then - ** we are dealing with a malformed record. - */ - if( idx!=szHdr || offset!=payloadSize ){ - sqliteFree(aType); - if( pC ) pC->aType = 0; - rc = SQLITE_CORRUPT; - break; - } - - /* Remember all aType and aColumn information if we have a cursor - ** to remember it in. */ - if( pC ){ - pC->payloadSize = payloadSize; - pC->aType = aType; - pC->aOffset = aOffset; - pC->cacheValid = 1; - } - } - - /* Get the column information. - */ - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( zRec ){ - zData = &zRec[aOffset[p2]]; - }else{ - len = sqlite3VdbeSerialTypeLen(aType[p2]); - sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->keyAsData, &sMem); - zData = sMem.z; - } - sqlite3VdbeSerialGet(zData, aType[p2], pTos); - pTos->enc = db->enc; - - /* If we dynamically allocated space to hold the data (in the - ** sqlite3VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the pTos structure rather. - ** This prevents a memory copy. - */ - if( (sMem.flags & MEM_Dyn)!=0 ){ - assert( pTos->flags & MEM_Ephem ); - assert( pTos->flags & (MEM_Str|MEM_Blob) ); - assert( pTos->z==sMem.z ); - assert( sMem.flags & MEM_Term ); - pTos->flags &= ~MEM_Ephem; - pTos->flags |= MEM_Dyn|MEM_Term; - } - - /* pTos->z might be pointing to sMem.zShort[]. Fix that so that we - ** can abandon sMem */ - rc = sqlite3VdbeMemMakeWriteable(pTos); - - /* Release the aType[] memory if we are not dealing with cursor */ - if( !pC ){ - sqliteFree(aType); - } - break; -} - -/* Opcode MakeRecord P1 P2 P3 -** -** Convert the top abs(P1) entries of the stack into a single entry -** suitable for use as a data record in a database table or as a key -** in an index. The details of the format are irrelavant as long as -** the OP_Column opcode can decode the record later and as long as the -** sqlite3VdbeRecordCompare function will correctly compare two encoded -** records. Refer to source code comments for the details of the record -** format. -** -** The original stack entries are popped from the stack if P1>0 but -** remain on the stack if P1<0. -** -** The P2 argument is divided into two 16-bit words before it is processed. -** If the hi-word is non-zero, then an extra integer is read from the stack -** and appended to the record as a varint. If the low-word of P2 is not -** zero and one or more of the entries are NULL, then jump to the value of -** the low-word of P2. This feature can be used to skip a uniqueness test -** on indices. -** -** P3 may be a string that is P1 characters long. The nth character of the -** string indicates the column affinity that should be used for the nth -** field of the index key (i.e. the first character of P3 corresponds to the -** lowest element on the stack). -** -** Character Column affinity -** ------------------------------ -** 'n' NUMERIC -** 'i' INTEGER -** 't' TEXT -** 'o' NONE -** -** If P3 is NULL then all index fields have the affinity NONE. -*/ -case OP_MakeRecord: { - /* Assuming the record contains N fields, the record format looks - ** like this: - ** - ** ------------------------------------------------------------------------ - ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | - ** ------------------------------------------------------------------------ - ** - ** Data(0) is taken from the lowest element of the stack and data(N-1) is - ** the top of the stack. - ** - ** Each type field is a varint representing the serial type of the - ** corresponding data element (see sqlite3VdbeSerialType()). The - ** hdr-size field is also a varint which is the offset from the beginning - ** of the record to data0. - */ - unsigned char *zNewRecord; - unsigned char *zCsr; - Mem *pRec; - Mem *pRowid = 0; - int nData = 0; /* Number of bytes of data space */ - int nHdr = 0; /* Number of bytes of header space */ - int nByte = 0; /* Space required for this record */ - u32 serial_type; /* Type field */ - int containsNull = 0; /* True if any of the data fields are NULL */ - char zTemp[NBFS]; /* Space to hold small records */ - Mem *pData0; - - int leaveOnStack; /* If true, leave the entries on the stack */ - int nField; /* Number of fields in the record */ - int jumpIfNull; /* Jump here if non-zero and any entries are NULL. */ - int addRowid; /* True to append a rowid column at the end */ - char *zAffinity; /* The affinity string for the record */ - - leaveOnStack = ((pOp->p1<0)?1:0); - nField = pOp->p1 * (leaveOnStack?-1:1); - jumpIfNull = (pOp->p2 & 0x00FFFFFF); - addRowid = ((pOp->p2>>24) & 0x0000FFFF)?1:0; - zAffinity = pOp->p3; - - pData0 = &pTos[1-nField]; - assert( pData0>=p->aStack ); - containsNull = 0; - - /* Loop through the elements that will make up the record to figure - ** out how much space is required for the new record. - */ - for(pRec=pData0; pRec<=pTos; pRec++){ - if( zAffinity ){ - applyAffinity(pRec, zAffinity[pRec-pData0], db->enc); - } - if( pRec->flags&MEM_Null ){ - containsNull = 1; - } - serial_type = sqlite3VdbeSerialType(pRec); - nData += sqlite3VdbeSerialTypeLen(serial_type); - nHdr += sqlite3VarintLen(serial_type); - } - - /* If we have to append a varint rowid to this record, set 'rowid' - ** to the value of the rowid and increase nByte by the amount of space - ** required to store it and the 0x00 seperator byte. - */ - if( addRowid ){ - pRowid = &pTos[0-nField]; - assert( pRowid>=p->aStack ); - Integerify(pRowid); - serial_type = sqlite3VdbeSerialType(pRowid); - nData += sqlite3VdbeSerialTypeLen(serial_type); - nHdr += sqlite3VarintLen(serial_type); - } - - /* Add the initial header varint and total the size */ - nHdr += sqlite3VarintLen(nHdr); - nByte = nHdr+nData; - - /* Allocate space for the new record. */ - if( nByte>sizeof(zTemp) ){ - zNewRecord = sqliteMallocRaw(nByte); - if( !zNewRecord ){ - goto no_mem; - } - }else{ - zNewRecord = zTemp; - } - - /* Write the record */ - zCsr = zNewRecord; - zCsr += sqlite3PutVarint(zCsr, nHdr); - for(pRec=pData0; pRec<=pTos; pRec++){ - serial_type = sqlite3VdbeSerialType(pRec); - zCsr += sqlite3PutVarint(zCsr, serial_type); /* serial type */ - } - if( addRowid ){ - zCsr += sqlite3PutVarint(zCsr, sqlite3VdbeSerialType(pRowid)); - } - for(pRec=pData0; pRec<=pTos; pRec++){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRec); /* serial data */ - } - if( addRowid ){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRowid); - } - - /* If zCsr has not been advanced exactly nByte bytes, then one - ** of the sqlite3PutVarint() or sqlite3VdbeSerialPut() calls above - ** failed. This indicates a corrupted memory cell or code bug. - */ - if( zCsr!=(zNewRecord+nByte) ){ - rc = SQLITE_INTERNAL; - goto abort_due_to_error; - } - - /* Pop entries off the stack if required. Push the new record on. */ - if( !leaveOnStack ){ - popStack(&pTos, nField+addRowid); - } - pTos++; - pTos->n = nByte; - if( nByte<=sizeof(zTemp) ){ - assert( zNewRecord==(unsigned char *)zTemp ); - pTos->z = pTos->zShort; - memcpy(pTos->zShort, zTemp, nByte); - pTos->flags = MEM_Blob | MEM_Short; - }else{ - assert( zNewRecord!=(unsigned char *)zTemp ); - pTos->z = zNewRecord; - pTos->flags = MEM_Blob | MEM_Dyn; - pTos->xDel = 0; - } - - /* If a NULL was encountered and jumpIfNull is non-zero, take the jump. */ - if( jumpIfNull && containsNull ){ - pc = jumpIfNull - 1; - } - break; -} - -/* Opcode: Statement P1 * * -** -** Begin an individual statement transaction which is part of a larger -** BEGIN..COMMIT transaction. This is needed so that the statement -** can be rolled back after an error without having to roll back the -** entire transaction. The statement transaction will automatically -** commit when the VDBE halts. -** -** The statement is begun on the database file with index P1. The main -** database file has an index of 0 and the file used for temporary tables -** has an index of 1. -*/ -case OP_Statement: { - int i = pOp->p1; - Btree *pBt; - if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt) && !(db->autoCommit) ){ - assert( sqlite3BtreeIsInTrans(pBt) ); - if( !sqlite3BtreeIsInStmt(pBt) ){ - rc = sqlite3BtreeBeginStmt(pBt); - } - } - break; -} - -/* Opcode: AutoCommit P1 P2 * -** -** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll -** back any currently active btree transactions. If there are any active -** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails. -** -** This instruction causes the VM to halt. -*/ -case OP_AutoCommit: { - u8 i = pOp->p1; - u8 rollback = pOp->p2; - - assert( i==1 || i==0 ); - assert( i==1 || rollback==0 ); - - assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ - - if( db->activeVdbeCnt>1 && i && !db->autoCommit ){ - /* If this instruction implements a COMMIT or ROLLBACK, other VMs are - ** still running, and a transaction is active, return an error indicating - ** that the other VMs must complete first. - */ - sqlite3SetString(&p->zErrMsg, "cannot ", rollback?"rollback":"commit", - " transaction - SQL statements in progress", 0); - rc = SQLITE_ERROR; - }else if( i!=db->autoCommit ){ - db->autoCommit = i; - if( pOp->p2 ){ - assert( i==1 ); - sqlite3RollbackAll(db); - }else if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pTos = pTos; - p->pc = pc; - db->autoCommit = 1-i; - p->rc = SQLITE_BUSY; - return SQLITE_BUSY; - } - return SQLITE_DONE; - }else{ - sqlite3SetString(&p->zErrMsg, - (!i)?"cannot start a transaction within a transaction":( - (rollback)?"cannot rollback - no transaction is active": - "cannot commit - no transaction is active"), 0); - - rc = SQLITE_ERROR; - } - break; -} - -/* Opcode: Transaction P1 P2 * -** -** Begin a transaction. The transaction ends when a Commit or Rollback -** opcode is encountered. Depending on the ON CONFLICT setting, the -** transaction might also be rolled back if an error is encountered. -** -** P1 is the index of the database file on which the transaction is -** started. Index 0 is the main database file and index 1 is the -** file used for temporary tables. -** -** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is -** obtained on the database file when a write-transaction is started. No -** other process can start another write transaction while this transaction is -** underway. Starting a write transaction also creates a rollback journal. A -** write transaction must be started before any changes can be made to the -** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained -** on the file. -** -** If P2 is zero, then a read-lock is obtained on the database file. -*/ -case OP_Transaction: { - int i = pOp->p1; - Btree *pBt; - - assert( i>=0 && i<db->nDb ); - pBt = db->aDb[i].pBt; - - if( pBt ){ - rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); - if( rc==SQLITE_BUSY ){ - p->pc = pc; - p->rc = SQLITE_BUSY; - p->pTos = pTos; - return SQLITE_BUSY; - } - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){ - goto abort_due_to_error; - } - } - break; -} - -/* Opcode: ReadCookie P1 P2 * -** -** Read cookie number P2 from database P1 and push it onto the stack. -** P2==0 is the schema version. P2==1 is the database format. -** P2==2 is the recommended pager cache size, and so forth. P1==0 is -** the main database file and P1==1 is the database file used to store -** temporary tables. -** -** There must be a read-lock on the database (either a transaction -** must be started or there must be an open cursor) before -** executing this instruction. -*/ -case OP_ReadCookie: { - int iMeta; - assert( pOp->p2<SQLITE_N_BTREE_META ); - assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( db->aDb[pOp->p1].pBt!=0 ); - /* The indexing of meta values at the schema layer is off by one from - ** the indexing in the btree layer. The btree considers meta[0] to - ** be the number of free pages in the database (a read-only value) - ** and meta[1] to be the schema cookie. The schema layer considers - ** meta[1] to be the schema cookie. So we have to shift the index - ** by one in the following statement. - */ - rc = sqlite3BtreeGetMeta(db->aDb[pOp->p1].pBt, 1 + pOp->p2, (u32 *)&iMeta); - pTos++; - pTos->i = iMeta; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: SetCookie P1 P2 * -** -** Write the top of the stack into cookie number P2 of database P1. -** P2==0 is the schema version. P2==1 is the database format. -** P2==2 is the recommended pager cache size, and so forth. P1==0 is -** the main database file and P1==1 is the database file used to store -** temporary tables. -** -** A transaction must be started before executing this opcode. -*/ -case OP_SetCookie: { - Db *pDb; - assert( pOp->p2<SQLITE_N_BTREE_META ); - assert( pOp->p1>=0 && pOp->p1<db->nDb ); - pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - assert( pTos>=p->aStack ); - Integerify(pTos); - /* See note about index shifting on OP_ReadCookie */ - rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i); - if( pOp->p2==0 ){ - /* When the schema cookie changes, record the new cookie internally */ - pDb->schema_cookie = pTos->i; - db->flags |= SQLITE_InternChanges; - } - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - break; -} - -/* Opcode: VerifyCookie P1 P2 * -** -** Check the value of global database parameter number 0 (the -** schema version) and make sure it is equal to P2. -** P1 is the database number which is 0 for the main database file -** and 1 for the file holding temporary tables and some higher number -** for auxiliary databases. -** -** The cookie changes its value whenever the database schema changes. -** This operation is used to detect when that the cookie has changed -** and that the current process needs to reread the schema. -** -** Either a transaction needs to have been started or an OP_Open needs -** to be executed (to establish a read lock) before this opcode is -** invoked. -*/ -case OP_VerifyCookie: { - int iMeta; - Btree *pBt; - assert( pOp->p1>=0 && pOp->p1<db->nDb ); - pBt = db->aDb[pOp->p1].pBt; - if( pBt ){ - rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&iMeta); - }else{ - rc = SQLITE_OK; - iMeta = 0; - } - if( rc==SQLITE_OK && iMeta!=pOp->p2 ){ - sqlite3SetString(&p->zErrMsg, "database schema has changed", (char*)0); - rc = SQLITE_SCHEMA; - } - break; -} - -/* Opcode: OpenRead P1 P2 P3 -** -** Open a read-only cursor for the database table whose root page is -** P2 in a database file. The database file is determined by an -** integer from the top of the stack. 0 means the main database and -** 1 means the database used for temporary tables. Give the new -** cursor an identifier of P1. The P1 values need not be contiguous -** but all P1 values should be small integers. It is an error for -** P1 to be negative. -** -** If P2==0 then take the root page number from the next of the stack. -** -** There will be a read lock on the database whenever there is an -** open cursor. If the database was unlocked prior to this instruction -** then a read lock is acquired as part of this instruction. A read -** lock allows other processes to read the database but prohibits -** any other process from modifying the database. The read lock is -** released when all cursors are closed. If this instruction attempts -** to get a read lock but fails, the script terminates with an -** SQLITE_BUSY error code. -** -** The P3 value is a pointer to a KeyInfo structure that defines the -** content and collating sequence of indices. P3 is NULL for cursors -** that are not pointing to indices. -** -** See also OpenWrite. -*/ -/* Opcode: OpenWrite P1 P2 P3 -** -** Open a read/write cursor named P1 on the table or index whose root -** page is P2. If P2==0 then take the root page number from the stack. -** -** The P3 value is a pointer to a KeyInfo structure that defines the -** content and collating sequence of indices. P3 is NULL for cursors -** that are not pointing to indices. -** -** This instruction works just like OpenRead except that it opens the cursor -** in read/write mode. For a given table, there can be one or more read-only -** cursors or a single read/write cursor but not both. -** -** See also OpenRead. -*/ -case OP_OpenRead: -case OP_OpenWrite: { - int i = pOp->p1; - int p2 = pOp->p2; - int wrFlag; - Btree *pX; - int iDb; - Cursor *pCur; - - assert( pTos>=p->aStack ); - Integerify(pTos); - iDb = pTos->i; - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - assert( iDb>=0 && iDb<db->nDb ); - pX = db->aDb[iDb].pBt; - assert( pX!=0 ); - wrFlag = pOp->opcode==OP_OpenWrite; - if( p2<=0 ){ - assert( pTos>=p->aStack ); - Integerify(pTos); - p2 = pTos->i; - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - if( p2<2 ){ - sqlite3SetString(&p->zErrMsg, "root page number less than 2", (char*)0); - rc = SQLITE_INTERNAL; - break; - } - } - assert( i>=0 ); - pCur = allocateCursor(p, i); - if( pCur==0 ) goto no_mem; - pCur->nullRow = 1; - if( pX==0 ) break; - /* We always provide a key comparison function. If the table being - ** opened is of type INTKEY, the comparision function will be ignored. */ - rc = sqlite3BtreeCursor(pX, p2, wrFlag, - sqlite3VdbeRecordCompare, pOp->p3, - &pCur->pCursor); - pCur->pKeyInfo = (KeyInfo*)pOp->p3; - if( pCur->pKeyInfo ){ - pCur->pIncrKey = &pCur->pKeyInfo->incrKey; - pCur->pKeyInfo->enc = p->db->enc; - }else{ - pCur->pIncrKey = &pCur->bogusIncrKey; - } - switch( rc ){ - case SQLITE_BUSY: { - p->pc = pc; - p->rc = SQLITE_BUSY; - p->pTos = &pTos[1 + (pOp->p2<=0)]; /* Operands must remain on stack */ - return SQLITE_BUSY; - } - case SQLITE_OK: { - int flags = sqlite3BtreeFlags(pCur->pCursor); - pCur->intKey = (flags & BTREE_INTKEY)!=0; - pCur->zeroData = (flags & BTREE_ZERODATA)!=0; - break; - } - case SQLITE_EMPTY: { - rc = SQLITE_OK; - break; - } - default: { - goto abort_due_to_error; - } - } - break; -} - -/* Opcode: OpenTemp P1 * P3 -** -** Open a new cursor to a transient table. -** The transient cursor is always opened read/write even if -** the main database is read-only. The transient table is deleted -** automatically when the cursor is closed. -** -** The cursor points to a BTree table if P3==0 and to a BTree index -** if P3 is not 0. If P3 is not NULL, it points to a KeyInfo structure -** that defines the format of keys in the index. -** -** This opcode is used for tables that exist for the duration of a single -** SQL statement only. Tables created using CREATE TEMPORARY TABLE -** are opened using OP_OpenRead or OP_OpenWrite. "Temporary" in the -** context of this opcode means for the duration of a single SQL statement -** whereas "Temporary" in the context of CREATE TABLE means for the duration -** of the connection to the database. Same word; different meanings. -*/ -case OP_OpenTemp: { - int i = pOp->p1; - Cursor *pCx; - assert( i>=0 ); - pCx = allocateCursor(p, i); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - rc = sqlite3BtreeFactory(db, 0, 1, TEMP_PAGES, &pCx->pBt); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(pCx->pBt, 1); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_ZERODATA flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an INTKEY table). - */ - if( pOp->p3 ){ - int pgno; - assert( pOp->p3type==P3_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, sqlite3VdbeRecordCompare, - pOp->p3, &pCx->pCursor); - pCx->pKeyInfo = (KeyInfo*)pOp->p3; - pCx->pKeyInfo->enc = p->db->enc; - pCx->pIncrKey = &pCx->pKeyInfo->incrKey; - } - }else{ - rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, 0, &pCx->pCursor); - pCx->intKey = 1; - pCx->pIncrKey = &pCx->bogusIncrKey; - } - } - break; -} - -/* Opcode: OpenPseudo P1 * * -** -** Open a new cursor that points to a fake table that contains a single -** row of data. Any attempt to write a second row of data causes the -** first row to be deleted. All data is deleted when the cursor is -** closed. -** -** A pseudo-table created by this opcode is useful for holding the -** NEW or OLD tables in a trigger. -*/ -case OP_OpenPseudo: { - int i = pOp->p1; - Cursor *pCx; - assert( i>=0 ); - pCx = allocateCursor(p, i); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->pseudoTable = 1; - pCx->pIncrKey = &pCx->bogusIncrKey; - break; -} - -/* Opcode: Close P1 * * -** -** Close a cursor previously opened as P1. If P1 is not -** currently open, this instruction is a no-op. -*/ -case OP_Close: { - int i = pOp->p1; - if( i>=0 && i<p->nCursor ){ - sqlite3VdbeFreeCursor(p->apCsr[i]); - p->apCsr[i] = 0; - } - break; -} - -/* Opcode: MoveGe P1 P2 * -** -** Pop the top of the stack and use its value as a key. Reposition -** cursor P1 so that it points to the smallest entry that is greater -** than or equal to the key that was popped ffrom the stack. -** If there are no records greater than or equal to the key and P2 -** is not zero, then jump to P2. -** -** See also: Found, NotFound, Distinct, MoveLt, MoveGt, MoveLe -*/ -/* Opcode: MoveGt P1 P2 * -** -** Pop the top of the stack and use its value as a key. Reposition -** cursor P1 so that it points to the smallest entry that is greater -** than the key from the stack. -** If there are no records greater than the key and P2 is not zero, -** then jump to P2. -** -** See also: Found, NotFound, Distinct, MoveLt, MoveGe, MoveLe -*/ -/* Opcode: MoveLt P1 P2 * -** -** Pop the top of the stack and use its value as a key. Reposition -** cursor P1 so that it points to the largest entry that is less -** than the key from the stack. -** If there are no records less than the key and P2 is not zero, -** then jump to P2. -** -** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLe -*/ -/* Opcode: MoveLe P1 P2 * -** -** Pop the top of the stack and use its value as a key. Reposition -** cursor P1 so that it points to the largest entry that is less than -** or equal to the key that was popped from the stack. -** If there are no records less than or eqal to the key and P2 is not zero, -** then jump to P2. -** -** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt -*/ -case OP_MoveLt: -case OP_MoveLe: -case OP_MoveGe: -case OP_MoveGt: { - int i = pOp->p1; - Cursor *pC; - - assert( pTos>=p->aStack ); - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - if( pC->pCursor!=0 ){ - int res, oc; - oc = pOp->opcode; - pC->nullRow = 0; - *pC->pIncrKey = oc==OP_MoveGt || oc==OP_MoveLe; - if( pC->intKey ){ - i64 iKey; - assert( !pOp->p3 ); - Integerify(pTos); - iKey = intToKey(pTos->i); - if( pOp->p2==0 && pOp->opcode==OP_MoveGe ){ - pC->movetoTarget = iKey; - pC->deferredMoveto = 1; - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - break; - } - sqlite3BtreeMoveto(pC->pCursor, 0, (u64)iKey, &res); - pC->lastRecno = pTos->i; - pC->recnoIsValid = res==0; - }else{ - Stringify(pTos, db->enc); - sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res); - pC->recnoIsValid = 0; - } - pC->deferredMoveto = 0; - pC->cacheValid = 0; - *pC->pIncrKey = 0; - sqlite3_search_count++; - if( oc==OP_MoveGe || oc==OP_MoveGt ){ - if( res<0 ){ - sqlite3BtreeNext(pC->pCursor, &res); - pC->recnoIsValid = 0; - }else{ - res = 0; - } - }else{ - assert( oc==OP_MoveLt || oc==OP_MoveLe ); - if( res>=0 ){ - sqlite3BtreePrevious(pC->pCursor, &res); - pC->recnoIsValid = 0; - }else{ - /* res might be negative because the table is empty. Check to - ** see if this is the case. - */ - res = sqlite3BtreeEof(pC->pCursor); - } - } - if( res ){ - if( pOp->p2>0 ){ - pc = pOp->p2 - 1; - }else{ - pC->nullRow = 1; - } - } - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: Distinct P1 P2 * -** -** Use the top of the stack as a string key. If a record with that key does -** not exist in the table of cursor P1, then jump to P2. If the record -** does already exist, then fall thru. The cursor is left pointing -** at the record if it exists. The key is not popped from the stack. -** -** This operation is similar to NotFound except that this operation -** does not pop the key from the stack. -** -** See also: Found, NotFound, MoveTo, IsUnique, NotExists -*/ -/* Opcode: Found P1 P2 * -** -** Use the top of the stack as a string key. If a record with that key -** does exist in table of P1, then jump to P2. If the record -** does not exist, then fall thru. The cursor is left pointing -** to the record if it exists. The key is popped from the stack. -** -** See also: Distinct, NotFound, MoveTo, IsUnique, NotExists -*/ -/* Opcode: NotFound P1 P2 * -** -** Use the top of the stack as a string key. If a record with that key -** does not exist in table of P1, then jump to P2. If the record -** does exist, then fall thru. The cursor is left pointing to the -** record if it exists. The key is popped from the stack. -** -** The difference between this operation and Distinct is that -** Distinct does not pop the key from the stack. -** -** See also: Distinct, Found, MoveTo, NotExists, IsUnique -*/ -case OP_Distinct: -case OP_NotFound: -case OP_Found: { - int i = pOp->p1; - int alreadyExists = 0; - Cursor *pC; - assert( pTos>=p->aStack ); - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - if( (pC = p->apCsr[i])->pCursor!=0 ){ - int res, rx; - assert( pC->intKey==0 ); - Stringify(pTos, db->enc); - rx = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res); - alreadyExists = rx==SQLITE_OK && res==0; - pC->deferredMoveto = 0; - pC->cacheValid = 0; - } - if( pOp->opcode==OP_Found ){ - if( alreadyExists ) pc = pOp->p2 - 1; - }else{ - if( !alreadyExists ) pc = pOp->p2 - 1; - } - if( pOp->opcode!=OP_Distinct ){ - Release(pTos); - pTos--; - } - break; -} - -/* Opcode: IsUnique P1 P2 * -** -** The top of the stack is an integer record number. Call this -** record number R. The next on the stack is an index key created -** using MakeIdxKey. Call it K. This instruction pops R from the -** stack but it leaves K unchanged. -** -** P1 is an index. So it has no data and its key consists of a -** record generated by OP_MakeIdxKey. This key contains one or more -** fields followed by a ROWID field. -** -** This instruction asks if there is an entry in P1 where the -** fields matches K but the rowid is different from R. -** If there is no such entry, then there is an immediate -** jump to P2. If any entry does exist where the index string -** matches K but the record number is not R, then the record -** number for that entry is pushed onto the stack and control -** falls through to the next instruction. -** -** See also: Distinct, NotFound, NotExists, Found -*/ -case OP_IsUnique: { - int i = pOp->p1; - Mem *pNos = &pTos[-1]; - Cursor *pCx; - BtCursor *pCrsr; - i64 R; - - /* Pop the value R off the top of the stack - */ - assert( pNos>=p->aStack ); - Integerify(pTos); - R = pTos->i; - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - assert( i>=0 && i<=p->nCursor ); - pCx = p->apCsr[i]; - assert( pCx!=0 ); - pCrsr = pCx->pCursor; - if( pCrsr!=0 ){ - int res, rc; - i64 v; /* The record number on the P1 entry that matches K */ - char *zKey; /* The value of K */ - int nKey; /* Number of bytes in K */ - int len; /* Number of bytes in K without the rowid at the end */ - int szRowid; /* Size of the rowid column at the end of zKey */ - - /* Make sure K is a string and make zKey point to K - */ - Stringify(pNos, db->enc); - zKey = pNos->z; - nKey = pNos->n; - - szRowid = sqlite3VdbeIdxRowidLen(nKey, zKey); - len = nKey-szRowid; - - /* Search for an entry in P1 where all but the last four bytes match K. - ** If there is no such entry, jump immediately to P2. - */ - assert( pCx->deferredMoveto==0 ); - pCx->cacheValid = 0; - rc = sqlite3BtreeMoveto(pCrsr, zKey, len, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - if( res<0 ){ - rc = sqlite3BtreeNext(pCrsr, &res); - if( res ){ - pc = pOp->p2 - 1; - break; - } - } - rc = sqlite3VdbeIdxKeyCompare(pCx, len, zKey, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - if( res>0 ){ - pc = pOp->p2 - 1; - break; - } - - /* At this point, pCrsr is pointing to an entry in P1 where all but - ** the final entry (the rowid) matches K. Check to see if the - ** final rowid column is different from R. If it equals R then jump - ** immediately to P2. - */ - rc = sqlite3VdbeIdxRowid(pCrsr, &v); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( v==R ){ - pc = pOp->p2 - 1; - break; - } - - /* The final varint of the key is different from R. Push it onto - ** the stack. (The record number of an entry that violates a UNIQUE - ** constraint.) - */ - pTos++; - pTos->i = v; - pTos->flags = MEM_Int; - } - break; -} - -/* Opcode: NotExists P1 P2 * -** -** Use the top of the stack as a integer key. If a record with that key -** does not exist in table of P1, then jump to P2. If the record -** does exist, then fall thru. The cursor is left pointing to the -** record if it exists. The integer key is popped from the stack. -** -** The difference between this operation and NotFound is that this -** operation assumes the key is an integer and NotFound assumes it -** is a string. -** -** See also: Distinct, Found, MoveTo, NotFound, IsUnique -*/ -case OP_NotExists: { - int i = pOp->p1; - Cursor *pC; - BtCursor *pCrsr; - assert( pTos>=p->aStack ); - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - int res, rx; - u64 iKey; - assert( pTos->flags & MEM_Int ); - assert( p->apCsr[i]->intKey ); - iKey = intToKey(pTos->i); - rx = sqlite3BtreeMoveto(pCrsr, 0, iKey, &res); - pC->lastRecno = pTos->i; - pC->recnoIsValid = res==0; - pC->nullRow = 0; - pC->cacheValid = 0; - if( rx!=SQLITE_OK || res!=0 ){ - pc = pOp->p2 - 1; - pC->recnoIsValid = 0; - } - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: NewRecno P1 * * -** -** Get a new integer record number used as the key to a table. -** The record number is not previously used as a key in the database -** table that cursor P1 points to. The new record number is pushed -** onto the stack. -*/ -case OP_NewRecno: { - int i = pOp->p1; - i64 v = 0; - Cursor *pC; - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - if( (pC = p->apCsr[i])->pCursor==0 ){ - /* The zero initialization above is all that is needed */ - }else{ - /* The next rowid or record number (different terms for the same - ** thing) is obtained in a two-step algorithm. - ** - ** First we attempt to find the largest existing rowid and add one - ** to that. But if the largest existing rowid is already the maximum - ** positive integer, we have to fall through to the second - ** probabilistic algorithm - ** - ** The second algorithm is to select a rowid at random and see if - ** it already exists in the table. If it does not exist, we have - ** succeeded. If the random rowid does exist, we select a new one - ** and try again, up to 1000 times. - ** - ** For a table with less than 2 billion entries, the probability - ** of not finding a unused rowid is about 1.0e-300. This is a - ** non-zero probability, but it is still vanishingly small and should - ** never cause a problem. You are much, much more likely to have a - ** hardware failure than for this algorithm to fail. - ** - ** The analysis in the previous paragraph assumes that you have a good - ** source of random numbers. Is a library function like lrand48() - ** good enough? Maybe. Maybe not. It's hard to know whether there - ** might be subtle bugs is some implementations of lrand48() that - ** could cause problems. To avoid uncertainty, SQLite uses its own - ** random number generator based on the RC4 algorithm. - ** - ** To promote locality of reference for repetitive inserts, the - ** first few attempts at chosing a random rowid pick values just a little - ** larger than the previous rowid. This has been shown experimentally - ** to double the speed of the COPY operation. - */ - int res, rx=SQLITE_OK, cnt; - i64 x; - cnt = 0; - assert( (sqlite3BtreeFlags(pC->pCursor) & BTREE_INTKEY)!=0 ); - assert( (sqlite3BtreeFlags(pC->pCursor) & BTREE_ZERODATA)==0 ); - if( !pC->useRandomRowid ){ - if( pC->nextRowidValid ){ - v = pC->nextRowid; - }else{ - rx = sqlite3BtreeLast(pC->pCursor, &res); - if( res ){ - v = 1; - }else{ - sqlite3BtreeKeySize(pC->pCursor, &v); - v = keyToInt(v); - if( v==0x7fffffffffffffff ){ - pC->useRandomRowid = 1; - }else{ - v++; - } - } - } - if( v<0x7fffffffffffffff ){ - pC->nextRowidValid = 1; - pC->nextRowid = v+1; - }else{ - pC->nextRowidValid = 0; - } - } - if( pC->useRandomRowid ){ - v = db->priorNewRowid; - cnt = 0; - do{ - if( v==0 || cnt>2 ){ - sqlite3Randomness(sizeof(v), &v); - if( cnt<5 ) v &= 0xffffff; - }else{ - unsigned char r; - sqlite3Randomness(1, &r); - v += r + 1; - } - if( v==0 ) continue; - x = intToKey(v); - rx = sqlite3BtreeMoveto(pC->pCursor, 0, (u64)x, &res); - cnt++; - }while( cnt<1000 && rx==SQLITE_OK && res==0 ); - db->priorNewRowid = v; - if( rx==SQLITE_OK && res==0 ){ - rc = SQLITE_FULL; - goto abort_due_to_error; - } - } - pC->recnoIsValid = 0; - pC->deferredMoveto = 0; - pC->cacheValid = 0; - } - pTos++; - pTos->i = v; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: PutIntKey P1 P2 * -** -** Write an entry into the table of cursor P1. A new entry is -** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value on the top of the -** stack. The key is the next value down on the stack. The key must -** be an integer. The stack is popped twice by this instruction. -** -** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is -** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P2 is set, -** then rowid is stored for subsequent return by the -** sqlite3_last_insert_rowid() function (otherwise it's unmodified). -*/ -/* Opcode: PutStrKey P1 * * -** -** Write an entry into the table of cursor P1. A new entry is -** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value on the top of the -** stack. The key is the next value down on the stack. The key must -** be a string. The stack is popped twice by this instruction. -** -** P1 may not be a pseudo-table opened using the OpenPseudo opcode. -*/ -case OP_PutIntKey: -case OP_PutStrKey: { - Mem *pNos = &pTos[-1]; - int i = pOp->p1; - Cursor *pC; - assert( pNos>=p->aStack ); - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - if( ((pC = p->apCsr[i])->pCursor!=0 || pC->pseudoTable) ){ - char *zKey; - i64 nKey; - i64 iKey; - if( pOp->opcode==OP_PutStrKey ){ - Stringify(pNos, db->enc); - nKey = pNos->n; - zKey = pNos->z; - }else{ - assert( pNos->flags & MEM_Int ); - - /* If the table is an INTKEY table, set nKey to the value of - ** the integer key, and zKey to NULL. Otherwise, set nKey to - ** sizeof(i64) and point zKey at iKey. iKey contains the integer - ** key in the on-disk byte order. - */ - iKey = intToKey(pNos->i); - if( pC->intKey ){ - nKey = intToKey(pNos->i); - zKey = 0; - }else{ - nKey = sizeof(i64); - zKey = (char*)&iKey; - } - - if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p2 & OPFLAG_LASTROWID ) db->lastRowid = pNos->i; - if( pC->nextRowidValid && pTos->i>=pC->nextRowid ){ - pC->nextRowidValid = 0; - } - } - if( pTos->flags & MEM_Null ){ - pTos->z = 0; - pTos->n = 0; - }else{ - assert( pTos->flags & (MEM_Blob|MEM_Str) ); - } - if( pC->pseudoTable ){ - /* PutStrKey does not work for pseudo-tables. - ** The following assert makes sure we are not trying to use - ** PutStrKey on a pseudo-table - */ - assert( pOp->opcode==OP_PutIntKey ); - sqliteFree(pC->pData); - pC->iKey = iKey; - pC->nData = pTos->n; - if( pTos->flags & MEM_Dyn ){ - pC->pData = pTos->z; - pTos->flags = MEM_Null; - }else{ - pC->pData = sqliteMallocRaw( pC->nData+2 ); - if( !pC->pData ) goto no_mem; - memcpy(pC->pData, pTos->z, pC->nData); - pC->pData[pC->nData] = 0; - pC->pData[pC->nData+1] = 0; - } - pC->nullRow = 0; - }else{ - rc = sqlite3BtreeInsert(pC->pCursor, zKey, nKey, pTos->z, pTos->n); - } - pC->recnoIsValid = 0; - pC->deferredMoveto = 0; - pC->cacheValid = 0; - } - popStack(&pTos, 2); - break; -} - -/* Opcode: Delete P1 P2 * -** -** Delete the record at which the P1 cursor is currently pointing. -** -** The cursor will be left pointing at either the next or the previous -** record in the table. If it is left pointing at the next record, then -** the next Next instruction will be a no-op. Hence it is OK to delete -** a record from within an Next loop. -** -** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is -** incremented (otherwise not). -** -** If P1 is a pseudo-table, then this instruction is a no-op. -*/ -case OP_Delete: { - int i = pOp->p1; - Cursor *pC; - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - if( pC->pCursor!=0 ){ - sqlite3VdbeCursorMoveto(pC); - rc = sqlite3BtreeDelete(pC->pCursor); - pC->nextRowidValid = 0; - pC->cacheValid = 0; - } - if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; - break; -} - -/* Opcode: ResetCount P1 * * -** -** This opcode resets the VMs internal change counter to 0. If P1 is true, -** then the value of the change counter is copied to the database handle -** change counter (returned by subsequent calls to sqlite3_changes()) -** before it is reset. This is used by trigger programs. -*/ -case OP_ResetCount: { - if( pOp->p1 ){ - sqlite3VdbeSetChanges(db, p->nChange); - } - p->nChange = 0; - break; -} - -/* Opcode: KeyAsData P1 P2 * -** -** Turn the key-as-data mode for cursor P1 either on (if P2==1) or -** off (if P2==0). In key-as-data mode, the OP_Column opcode pulls -** data off of the key rather than the data. This is used for -** processing compound selects. -*/ -case OP_KeyAsData: { - int i = pOp->p1; - Cursor *pC; - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - pC->keyAsData = pOp->p2; - break; -} - -/* Opcode: RowData P1 * * -** -** Push onto the stack the complete row data for cursor P1. -** There is no interpretation of the data. It is just copied -** onto the stack exactly as it is found in the database file. -** -** If the cursor is not pointing to a valid row, a NULL is pushed -** onto the stack. -*/ -/* Opcode: RowKey P1 * * -** -** Push onto the stack the complete row key for cursor P1. -** There is no interpretation of the key. It is just copied -** onto the stack exactly as it is found in the database file. -** -** If the cursor is not pointing to a valid row, a NULL is pushed -** onto the stack. -*/ -case OP_RowKey: -case OP_RowData: { - int i = pOp->p1; - Cursor *pC; - u32 n; - - pTos++; - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - if( pC->nullRow ){ - pTos->flags = MEM_Null; - }else if( pC->pCursor!=0 ){ - BtCursor *pCrsr = pC->pCursor; - sqlite3VdbeCursorMoveto(pC); - if( pC->nullRow ){ - pTos->flags = MEM_Null; - break; - }else if( pC->keyAsData || pOp->opcode==OP_RowKey ){ - i64 n64; - assert( !pC->intKey ); - sqlite3BtreeKeySize(pCrsr, &n64); - n = n64; - }else{ - sqlite3BtreeDataSize(pCrsr, &n); - } - pTos->n = n; - if( n<=NBFS ){ - pTos->flags = MEM_Blob | MEM_Short; - pTos->z = pTos->zShort; - }else{ - char *z = sqliteMallocRaw( n ); - if( z==0 ) goto no_mem; - pTos->flags = MEM_Blob | MEM_Dyn; - pTos->xDel = 0; - pTos->z = z; - } - if( pC->keyAsData || pOp->opcode==OP_RowKey ){ - sqlite3BtreeKey(pCrsr, 0, n, pTos->z); - }else{ - sqlite3BtreeData(pCrsr, 0, n, pTos->z); - } - }else if( pC->pseudoTable ){ - pTos->n = pC->nData; - pTos->z = pC->pData; - pTos->flags = MEM_Blob|MEM_Ephem; - }else{ - pTos->flags = MEM_Null; - } - break; -} - -/* Opcode: Recno P1 * * -** -** Push onto the stack an integer which is the first 4 bytes of the -** the key to the current entry in a sequential scan of the database -** file P1. The sequential scan should have been started using the -** Next opcode. -*/ -case OP_Recno: { - int i = pOp->p1; - Cursor *pC; - i64 v; - - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - sqlite3VdbeCursorMoveto(pC); - pTos++; - if( pC->recnoIsValid ){ - v = pC->lastRecno; - }else if( pC->pseudoTable ){ - v = keyToInt(pC->iKey); - }else if( pC->nullRow || pC->pCursor==0 ){ - pTos->flags = MEM_Null; - break; - }else{ - assert( pC->pCursor!=0 ); - sqlite3BtreeKeySize(pC->pCursor, &v); - v = keyToInt(v); - } - pTos->i = v; - pTos->flags = MEM_Int; - break; -} - -/* Opcode: FullKey P1 * * -** -** Extract the complete key from the record that cursor P1 is currently -** pointing to and push the key onto the stack as a string. -** -** Compare this opcode to Recno. The Recno opcode extracts the first -** 4 bytes of the key and pushes those bytes onto the stack as an -** integer. This instruction pushes the entire key as a string. -** -** This opcode may not be used on a pseudo-table. -*/ -case OP_FullKey: { - int i = pOp->p1; - BtCursor *pCrsr; - Cursor *pC; - - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - assert( p->apCsr[i]->keyAsData ); - assert( !p->apCsr[i]->pseudoTable ); - pTos++; - pTos->flags = MEM_Null; - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - i64 amt; - char *z; - - sqlite3VdbeCursorMoveto(pC); - assert( pC->intKey==0 ); - sqlite3BtreeKeySize(pCrsr, &amt); - if( amt<=0 ){ - rc = SQLITE_CORRUPT; - goto abort_due_to_error; - } - if( amt>NBFS ){ - z = sqliteMallocRaw( amt ); - if( z==0 ) goto no_mem; - pTos->flags = MEM_Blob | MEM_Dyn; - pTos->xDel = 0; - }else{ - z = pTos->zShort; - pTos->flags = MEM_Blob | MEM_Short; - } - sqlite3BtreeKey(pCrsr, 0, amt, z); - pTos->z = z; - pTos->n = amt; - } - break; -} - -/* Opcode: NullRow P1 * * -** -** Move the cursor P1 to a null row. Any OP_Column operations -** that occur while the cursor is on the null row will always push -** a NULL onto the stack. -*/ -case OP_NullRow: { - int i = pOp->p1; - Cursor *pC; - - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - pC->nullRow = 1; - pC->recnoIsValid = 0; - break; -} - -/* Opcode: Last P1 P2 * -** -** The next use of the Recno or Column or Next instruction for P1 -** will refer to the last entry in the database table or index. -** If the table or index is empty and P2>0, then jump immediately to P2. -** If P2 is 0 or if the table or index is not empty, fall through -** to the following instruction. -*/ -case OP_Last: { - int i = pOp->p1; - Cursor *pC; - BtCursor *pCrsr; - - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - if( (pCrsr = pC->pCursor)!=0 ){ - int res; - rc = sqlite3BtreeLast(pCrsr, &res); - pC->nullRow = res; - pC->deferredMoveto = 0; - pC->cacheValid = 0; - if( res && pOp->p2>0 ){ - pc = pOp->p2 - 1; - } - }else{ - pC->nullRow = 0; - } - break; -} - -/* Opcode: Rewind P1 P2 * -** -** The next use of the Recno or Column or Next instruction for P1 -** will refer to the first entry in the database table or index. -** If the table or index is empty and P2>0, then jump immediately to P2. -** If P2 is 0 or if the table or index is not empty, fall through -** to the following instruction. -*/ -case OP_Rewind: { - int i = pOp->p1; - Cursor *pC; - BtCursor *pCrsr; - int res; - - assert( i>=0 && i<p->nCursor ); - pC = p->apCsr[i]; - assert( pC!=0 ); - if( (pCrsr = pC->pCursor)!=0 ){ - rc = sqlite3BtreeFirst(pCrsr, &res); - pC->atFirst = res==0; - pC->deferredMoveto = 0; - pC->cacheValid = 0; - }else{ - res = 1; - } - pC->nullRow = res; - if( res && pOp->p2>0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: Next P1 P2 * -** -** Advance cursor P1 so that it points to the next key/data pair in its -** table or index. If there are no more key/value pairs then fall through -** to the following instruction. But if the cursor advance was successful, -** jump immediately to P2. -** -** See also: Prev -*/ -/* Opcode: Prev P1 P2 * -** -** Back up cursor P1 so that it points to the previous key/data pair in its -** table or index. If there is no previous key/value pairs then fall through -** to the following instruction. But if the cursor backup was successful, -** jump immediately to P2. -*/ -case OP_Prev: -case OP_Next: { - Cursor *pC; - BtCursor *pCrsr; - - CHECK_FOR_INTERRUPT; - assert( pOp->p1>=0 && pOp->p1<p->nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - if( (pCrsr = pC->pCursor)!=0 ){ - int res; - if( pC->nullRow ){ - res = 1; - }else{ - assert( pC->deferredMoveto==0 ); - rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) : - sqlite3BtreePrevious(pCrsr, &res); - pC->nullRow = res; - pC->cacheValid = 0; - } - if( res==0 ){ - pc = pOp->p2 - 1; - sqlite3_search_count++; - } - }else{ - pC->nullRow = 1; - } - pC->recnoIsValid = 0; - break; -} - -/* Opcode: IdxPut P1 P2 P3 -** -** The top of the stack holds a SQL index key made using the -** MakeIdxKey instruction. This opcode writes that key into the -** index P1. Data for the entry is nil. -** -** If P2==1, then the key must be unique. If the key is not unique, -** the program aborts with a SQLITE_CONSTRAINT error and the database -** is rolled back. If P3 is not null, then it becomes part of the -** error message returned with the SQLITE_CONSTRAINT. -*/ -case OP_IdxPut: { - int i = pOp->p1; - Cursor *pC; - BtCursor *pCrsr; - assert( pTos>=p->aStack ); - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - assert( pTos->flags & MEM_Blob ); - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - int nKey = pTos->n; - const char *zKey = pTos->z; - if( pOp->p2 ){ - int res; - int len; - - /* 'len' is the length of the key minus the rowid at the end */ - len = nKey - sqlite3VdbeIdxRowidLen(nKey, zKey); - - rc = sqlite3BtreeMoveto(pCrsr, zKey, len, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - while( res!=0 && !sqlite3BtreeEof(pCrsr) ){ - int c; - if( sqlite3VdbeIdxKeyCompare(pC, len, zKey, &c)==SQLITE_OK && c==0 ){ - rc = SQLITE_CONSTRAINT; - if( pOp->p3 && pOp->p3[0] ){ - sqlite3SetString(&p->zErrMsg, pOp->p3, (char*)0); - } - goto abort_due_to_error; - } - if( res<0 ){ - sqlite3BtreeNext(pCrsr, &res); - res = +1; - }else{ - break; - } - } - } - assert( pC->intKey==0 ); - rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0); - assert( pC->deferredMoveto==0 ); - pC->cacheValid = 0; - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: IdxDelete P1 * * -** -** The top of the stack is an index key built using the MakeIdxKey opcode. -** This opcode removes that entry from the index. -*/ -case OP_IdxDelete: { - int i = pOp->p1; - Cursor *pC; - BtCursor *pCrsr; - assert( pTos>=p->aStack ); - assert( pTos->flags & MEM_Blob ); - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - int rx, res; - rx = sqlite3BtreeMoveto(pCrsr, pTos->z, pTos->n, &res); - if( rx==SQLITE_OK && res==0 ){ - rc = sqlite3BtreeDelete(pCrsr); - } - assert( pC->deferredMoveto==0 ); - pC->cacheValid = 0; - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: IdxRecno P1 * * -** -** Push onto the stack an integer which is the varint located at the -** end of the index key pointed to by cursor P1. These integer should be -** the record number of the table entry to which this index entry points. -** -** See also: Recno, MakeIdxKey. -*/ -case OP_IdxRecno: { - int i = pOp->p1; - BtCursor *pCrsr; - Cursor *pC; - - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - pTos++; - pTos->flags = MEM_Null; - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - i64 rowid; - - assert( pC->deferredMoveto==0 ); - assert( pC->intKey==0 ); - if( pC->nullRow ){ - pTos->flags = MEM_Null; - }else{ - rc = sqlite3VdbeIdxRowid(pCrsr, &rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pTos->flags = MEM_Int; - pTos->i = rowid; - } - } - break; -} - -/* Opcode: IdxGT P1 P2 * -** -** The top of the stack is an index entry that omits the ROWID. Compare -** the top of stack against the index that P1 is currently pointing to. -** Ignore the ROWID on the P1 index. -** -** The top of the stack might have fewer columns that P1. -** -** If the P1 index entry is greater than the top of the stack -** then jump to P2. Otherwise fall through to the next instruction. -** In either case, the stack is popped once. -*/ -/* Opcode: IdxGE P1 P2 P3 -** -** The top of the stack is an index entry that omits the ROWID. Compare -** the top of stack against the index that P1 is currently pointing to. -** Ignore the ROWID on the P1 index. -** -** If the P1 index entry is greater than or equal to the top of the stack -** then jump to P2. Otherwise fall through to the next instruction. -** In either case, the stack is popped once. -** -** If P3 is the "+" string (or any other non-NULL string) then the -** index taken from the top of the stack is temporarily increased by -** an epsilon prior to the comparison. This make the opcode work -** like IdxGT except that if the key from the stack is a prefix of -** the key in the cursor, the result is false whereas it would be -** true with IdxGT. -*/ -/* Opcode: IdxLT P1 P2 P3 -** -** The top of the stack is an index entry that omits the ROWID. Compare -** the top of stack against the index that P1 is currently pointing to. -** Ignore the ROWID on the P1 index. -** -** If the P1 index entry is less than the top of the stack -** then jump to P2. Otherwise fall through to the next instruction. -** In either case, the stack is popped once. -** -** If P3 is the "+" string (or any other non-NULL string) then the -** index taken from the top of the stack is temporarily increased by -** an epsilon prior to the comparison. This makes the opcode work -** like IdxLE. -*/ -case OP_IdxLT: -case OP_IdxGT: -case OP_IdxGE: { - int i= pOp->p1; - BtCursor *pCrsr; - Cursor *pC; - - assert( i>=0 && i<p->nCursor ); - assert( p->apCsr[i]!=0 ); - assert( pTos>=p->aStack ); - if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ - int res, rc; - - assert( pTos->flags & MEM_Blob ); /* Created using OP_Make*Key */ - Stringify(pTos, db->enc); - assert( pC->deferredMoveto==0 ); - *pC->pIncrKey = pOp->p3!=0; - assert( pOp->p3==0 || pOp->opcode!=OP_IdxGT ); - rc = sqlite3VdbeIdxKeyCompare(pC, pTos->n, pTos->z, &res); - *pC->pIncrKey = 0; - if( rc!=SQLITE_OK ){ - break; - } - if( pOp->opcode==OP_IdxLT ){ - res = -res; - }else if( pOp->opcode==OP_IdxGE ){ - res++; - } - if( res>0 ){ - pc = pOp->p2 - 1 ; - } - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: IdxIsNull P1 P2 * -** -** The top of the stack contains an index entry such as might be generated -** by the MakeIdxKey opcode. This routine looks at the first P1 fields of -** that key. If any of the first P1 fields are NULL, then a jump is made -** to address P2. Otherwise we fall straight through. -** -** The index entry is always popped from the stack. -*/ -case OP_IdxIsNull: { - int i = pOp->p1; - int k, n; - const char *z; - u32 serial_type; - - assert( pTos>=p->aStack ); - assert( pTos->flags & MEM_Blob ); - z = pTos->z; - n = pTos->n; - k = sqlite3GetVarint32(z, &serial_type); - for(; k<n && i>0; i--){ - k += sqlite3GetVarint32(&z[k], &serial_type); - if( serial_type==0 ){ /* Serial type 0 is a NULL */ - pc = pOp->p2-1; - break; - } - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: Destroy P1 P2 * -** -** Delete an entire database table or index whose root page in the database -** file is given by P1. -** -** The table being destroyed is in the main database file if P2==0. If -** P2==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** See also: Clear -*/ -case OP_Destroy: { - rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1); - break; -} - -/* Opcode: Clear P1 P2 * -** -** Delete all contents of the database table or index whose root page -** in the database file is given by P1. But, unlike Destroy, do not -** remove the table or index from the database file. -** -** The table being clear is in the main database file if P2==0. If -** P2==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** See also: Destroy -*/ -case OP_Clear: { - rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1); - break; -} - -/* Opcode: CreateTable P1 * * -** -** Allocate a new table in the main database file if P2==0 or in the -** auxiliary database file if P2==1. Push the page number -** for the root page of the new table onto the stack. -** -** The difference between a table and an index is this: A table must -** have a 4-byte integer key and can have arbitrary data. An index -** has an arbitrary key but no data. -** -** See also: CreateIndex -*/ -/* Opcode: CreateIndex P1 * * -** -** Allocate a new index in the main database file if P2==0 or in the -** auxiliary database file if P2==1. Push the page number of the -** root page of the new index onto the stack. -** -** See documentation on OP_CreateTable for additional information. -*/ -case OP_CreateIndex: -case OP_CreateTable: { - int pgno; - int flags; - Db *pDb; - assert( pOp->p1>=0 && pOp->p1<db->nDb ); - pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - if( pOp->opcode==OP_CreateTable ){ - /* flags = BTREE_INTKEY; */ - flags = BTREE_LEAFDATA|BTREE_INTKEY; - }else{ - flags = BTREE_ZERODATA; - } - rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags); - pTos++; - if( rc==SQLITE_OK ){ - pTos->i = pgno; - pTos->flags = MEM_Int; - }else{ - pTos->flags = MEM_Null; - } - break; -} - -/* Opcode: ParseSchema P1 * P3 -** -** Read and parse all entries from the SQLITE_MASTER table of database P1 -** that match the WHERE clause P3. -** -** This opcode invokes the parser to create a new virtual machine, -** then runs the new virtual machine. It is thus a reentrant opcode. -*/ -case OP_ParseSchema: { - char *zSql; - int iDb = pOp->p1; - const char *zMaster; - InitData initData; - - assert( iDb>=0 && iDb<db->nDb ); - if( !DbHasProperty(db, iDb, DB_SchemaLoaded) ) break; - zMaster = iDb==1 ? TEMP_MASTER_NAME : MASTER_NAME; - initData.db = db; - initData.pzErrMsg = &p->zErrMsg; - zSql = sqlite3MPrintf( - "SELECT name, rootpage, sql, %d FROM '%q'.%s WHERE %s", - pOp->p1, db->aDb[iDb].zName, zMaster, pOp->p3); - if( zSql==0 ) goto no_mem; - sqlite3SafetyOff(db); - assert( db->init.busy==0 ); - db->init.busy = 1; - rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); - db->init.busy = 0; - sqlite3SafetyOn(db); - sqliteFree(zSql); - break; -} - -/* Opcode: DropTable P1 * P3 -** -** Remove the internal (in-memory) data structures that describe -** the table named P3 in database P1. This is called after a table -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTable: { - sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p3); - break; -} - -/* Opcode: DropIndex P1 * P3 -** -** Remove the internal (in-memory) data structures that describe -** the index named P3 in database P1. This is called after an index -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropIndex: { - sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p3); - break; -} - -/* Opcode: DropTrigger P1 * P3 -** -** Remove the internal (in-memory) data structures that describe -** the trigger named P3 in database P1. This is called after a trigger -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTrigger: { - sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p3); - break; -} - - -/* Opcode: IntegrityCk * P2 * -** -** Do an analysis of the currently open database. Push onto the -** stack the text of an error message describing any problems. -** If there are no errors, push a "ok" onto the stack. -** -** The root page numbers of all tables in the database are integer -** values on the stack. This opcode pulls as many integers as it -** can off of the stack and uses those numbers as the root pages. -** -** If P2 is not zero, the check is done on the auxiliary database -** file, not the main database file. -** -** This opcode is used for testing purposes only. -*/ -case OP_IntegrityCk: { - int nRoot; - int *aRoot; - int j; - char *z; - - for(nRoot=0; &pTos[-nRoot]>=p->aStack; nRoot++){ - if( (pTos[-nRoot].flags & MEM_Int)==0 ) break; - } - assert( nRoot>0 ); - aRoot = sqliteMallocRaw( sizeof(int*)*(nRoot+1) ); - if( aRoot==0 ) goto no_mem; - for(j=0; j<nRoot; j++){ - Mem *pMem = &pTos[-j]; - aRoot[j] = pMem->i; - } - aRoot[j] = 0; - popStack(&pTos, nRoot); - pTos++; - z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot); - if( z==0 || z[0]==0 ){ - if( z ) sqliteFree(z); - pTos->z = "ok"; - pTos->n = 2; - pTos->flags = MEM_Str | MEM_Static | MEM_Term; - }else{ - pTos->z = z; - pTos->n = strlen(z); - pTos->flags = MEM_Str | MEM_Dyn | MEM_Term; - pTos->xDel = 0; - } - pTos->enc = SQLITE_UTF8; - sqlite3VdbeChangeEncoding(pTos, db->enc); - sqliteFree(aRoot); - break; -} - -/* Opcode: ListWrite * * * -** -** Write the integer on the top of the stack -** into the temporary storage list. -*/ -case OP_ListWrite: { - Keylist *pKeylist; - assert( pTos>=p->aStack ); - pKeylist = p->pList; - if( pKeylist==0 || pKeylist->nUsed>=pKeylist->nKey ){ - pKeylist = sqliteMallocRaw( sizeof(Keylist)+999*sizeof(pKeylist->aKey[0]) ); - if( pKeylist==0 ) goto no_mem; - pKeylist->nKey = 1000; - pKeylist->nRead = 0; - pKeylist->nUsed = 0; - pKeylist->pNext = p->pList; - p->pList = pKeylist; - } - Integerify(pTos); - pKeylist->aKey[pKeylist->nUsed++] = pTos->i; - assert( (pTos->flags & MEM_Dyn)==0 ); - pTos--; - break; -} - -/* Opcode: ListRewind * * * -** -** Rewind the temporary buffer back to the beginning. -*/ -case OP_ListRewind: { - /* What this opcode codes, really, is reverse the order of the - ** linked list of Keylist structures so that they are read out - ** in the same order that they were read in. */ - Keylist *pRev, *pTop; - pRev = 0; - while( p->pList ){ - pTop = p->pList; - p->pList = pTop->pNext; - pTop->pNext = pRev; - pRev = pTop; - } - p->pList = pRev; - break; -} - -/* Opcode: ListRead * P2 * -** -** Attempt to read an integer from the temporary storage buffer -** and push it onto the stack. If the storage buffer is empty, -** push nothing but instead jump to P2. -*/ -case OP_ListRead: { - Keylist *pKeylist; - CHECK_FOR_INTERRUPT; - pKeylist = p->pList; - if( pKeylist!=0 ){ - assert( pKeylist->nRead>=0 ); - assert( pKeylist->nRead<pKeylist->nUsed ); - assert( pKeylist->nRead<pKeylist->nKey ); - pTos++; - pTos->i = pKeylist->aKey[pKeylist->nRead++]; - pTos->flags = MEM_Int; - if( pKeylist->nRead>=pKeylist->nUsed ){ - p->pList = pKeylist->pNext; - sqliteFree(pKeylist); - } - }else{ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: ListReset * * * -** -** Reset the temporary storage buffer so that it holds nothing. -*/ -case OP_ListReset: { - if( p->pList ){ - sqlite3VdbeKeylistFree(p->pList); - p->pList = 0; - } - break; -} - -/* Opcode: ContextPush * * * -** -** Save the current Vdbe context such that it can be restored by a ContextPop -** opcode. The context stores the last insert row id, the last statement change -** count, and the current statement change count. -*/ -case OP_ContextPush: { - int i = p->contextStackTop++; - Context *pContext; - - assert( i>=0 ); - /* FIX ME: This should be allocated as part of the vdbe at compile-time */ - if( i>=p->contextStackDepth ){ - p->contextStackDepth = i+1; - p->contextStack = sqliteRealloc(p->contextStack, sizeof(Context)*(i+1)); - if( p->contextStack==0 ) goto no_mem; - } - pContext = &p->contextStack[i]; - pContext->lastRowid = db->lastRowid; - pContext->nChange = p->nChange; - pContext->pList = p->pList; - p->pList = 0; - break; -} - -/* Opcode: ContextPop * * * -** -** Restore the Vdbe context to the state it was in when contextPush was last -** executed. The context stores the last insert row id, the last statement -** change count, and the current statement change count. -*/ -case OP_ContextPop: { - Context *pContext = &p->contextStack[--p->contextStackTop]; - assert( p->contextStackTop>=0 ); - db->lastRowid = pContext->lastRowid; - p->nChange = pContext->nChange; - sqlite3VdbeKeylistFree(p->pList); - p->pList = pContext->pList; - break; -} - -/* Opcode: SortPut * * * -** -** The TOS is the key and the NOS is the data. Pop both from the stack -** and put them on the sorter. The key and data should have been -** made using SortMakeKey and SortMakeRec, respectively. -*/ -case OP_SortPut: { - Mem *pNos = &pTos[-1]; - Sorter *pSorter; - assert( pNos>=p->aStack ); - if( Dynamicify(pTos, db->enc) ) goto no_mem; - pSorter = sqliteMallocRaw( sizeof(Sorter) ); - if( pSorter==0 ) goto no_mem; - pSorter->pNext = p->pSort; - p->pSort = pSorter; - assert( pTos->flags & MEM_Dyn ); - pSorter->nKey = pTos->n; - pSorter->zKey = pTos->z; - pSorter->data.flags = MEM_Null; - rc = sqlite3VdbeMemMove(&pSorter->data, pNos); - pTos -= 2; - break; -} - -/* Opcode: Sort * * P3 -** -** Sort all elements on the sorter. The algorithm is a -** mergesort. The P3 argument is a pointer to a KeyInfo structure -** that describes the keys to be sorted. -*/ -case OP_Sort: { - int i; - KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3; - Sorter *pElem; - Sorter *apSorter[NSORT]; - pKeyInfo->enc = p->db->enc; - for(i=0; i<NSORT; i++){ - apSorter[i] = 0; - } - while( p->pSort ){ - pElem = p->pSort; - p->pSort = pElem->pNext; - pElem->pNext = 0; - for(i=0; i<NSORT-1; i++){ - if( apSorter[i]==0 ){ - apSorter[i] = pElem; - break; - }else{ - pElem = Merge(apSorter[i], pElem, pKeyInfo); - apSorter[i] = 0; - } - } - if( i>=NSORT-1 ){ - apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem, pKeyInfo); - } - } - pElem = 0; - for(i=0; i<NSORT; i++){ - pElem = Merge(apSorter[i], pElem, pKeyInfo); - } - p->pSort = pElem; - break; -} - -/* Opcode: SortNext * P2 * -** -** Push the data for the topmost element in the sorter onto the -** stack, then remove the element from the sorter. If the sorter -** is empty, push nothing on the stack and instead jump immediately -** to instruction P2. -*/ -case OP_SortNext: { - Sorter *pSorter = p->pSort; - CHECK_FOR_INTERRUPT; - if( pSorter!=0 ){ - p->pSort = pSorter->pNext; - pTos++; - pTos->flags = MEM_Null; - rc = sqlite3VdbeMemMove(pTos, &pSorter->data); - sqliteFree(pSorter->zKey); - sqliteFree(pSorter); - }else{ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: SortReset * * * -** -** Remove any elements that remain on the sorter. -*/ -case OP_SortReset: { - sqlite3VdbeSorterReset(p); - break; -} - -/* Opcode: MemStore P1 P2 * -** -** Write the top of the stack into memory location P1. -** P1 should be a small integer since space is allocated -** for all memory locations between 0 and P1 inclusive. -** -** After the data is stored in the memory location, the -** stack is popped once if P2 is 1. If P2 is zero, then -** the original data remains on the stack. -*/ -case OP_MemStore: { - assert( pTos>=p->aStack ); - assert( pOp->p1>=0 && pOp->p1<p->nMem ); - rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], pTos); - pTos--; - - /* If P2 is 0 then fall thru to the next opcode, OP_MemLoad, that will - ** restore the top of the stack to its original value. - */ - if( pOp->p2 ){ - break; - } -} -/* Opcode: MemLoad P1 * * -** -** Push a copy of the value in memory location P1 onto the stack. -** -** If the value is a string, then the value pushed is a pointer to -** the string that is stored in the memory location. If the memory -** location is subsequently changed (using OP_MemStore) then the -** value pushed onto the stack will change too. -*/ -case OP_MemLoad: { - int i = pOp->p1; - assert( i>=0 && i<p->nMem ); - pTos++; - sqlite3VdbeMemShallowCopy(pTos, &p->aMem[i], MEM_Ephem); - break; -} - -/* Opcode: MemIncr P1 P2 * -** -** Increment the integer valued memory cell P1 by 1. If P2 is not zero -** and the result after the increment is greater than zero, then jump -** to P2. -** -** This instruction throws an error if the memory cell is not initially -** an integer. -*/ -case OP_MemIncr: { - int i = pOp->p1; - Mem *pMem; - assert( i>=0 && i<p->nMem ); - pMem = &p->aMem[i]; - assert( pMem->flags==MEM_Int ); - pMem->i++; - if( pOp->p2>0 && pMem->i>0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: AggReset P1 P2 P3 -** -** Reset the aggregator so that it no longer contains any data. -** Future aggregator elements will contain P2 values each and be sorted -** using the KeyInfo structure pointed to by P3. -** -** If P1 is non-zero, then only a single aggregator row is available (i.e. -** there is no GROUP BY expression). In this case it is illegal to invoke -** OP_AggFocus. -*/ -case OP_AggReset: { - assert( !pOp->p3 || pOp->p3type==P3_KEYINFO ); - if( pOp->p1 ){ - rc = sqlite3VdbeAggReset(0, &p->agg, (KeyInfo *)pOp->p3); - p->agg.nMem = pOp->p2; /* Agg.nMem is used by AggInsert() */ - rc = AggInsert(&p->agg, 0, 0); - }else{ - rc = sqlite3VdbeAggReset(db, &p->agg, (KeyInfo *)pOp->p3); - p->agg.nMem = pOp->p2; - } - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - p->agg.apFunc = sqliteMalloc( p->agg.nMem*sizeof(p->agg.apFunc[0]) ); - if( p->agg.apFunc==0 ) goto no_mem; - break; -} - -/* Opcode: AggInit * P2 P3 -** -** Initialize the function parameters for an aggregate function. -** The aggregate will operate out of aggregate column P2. -** P3 is a pointer to the FuncDef structure for the function. -*/ -case OP_AggInit: { - int i = pOp->p2; - assert( i>=0 && i<p->agg.nMem ); - p->agg.apFunc[i] = (FuncDef*)pOp->p3; - break; -} - -/* Opcode: AggFunc * P2 P3 -** -** Execute the step function for an aggregate. The -** function has P2 arguments. P3 is a pointer to the FuncDef -** structure that specifies the function. -** -** The top of the stack must be an integer which is the index of -** the aggregate column that corresponds to this aggregate function. -** Ideally, this index would be another parameter, but there are -** no free parameters left. The integer is popped from the stack. -*/ -case OP_AggFunc: { - int n = pOp->p2; - int i; - Mem *pMem, *pRec; - sqlite3_context ctx; - sqlite3_value **apVal; - - assert( n>=0 ); - assert( pTos->flags==MEM_Int ); - pRec = &pTos[-n]; - assert( pRec>=p->aStack ); - - apVal = p->apArg; - assert( apVal || n==0 ); - - for(i=0; i<n; i++, pRec++){ - apVal[i] = pRec; - storeTypeInfo(pRec, db->enc); - } - i = pTos->i; - assert( i>=0 && i<p->agg.nMem ); - ctx.pFunc = (FuncDef*)pOp->p3; - pMem = &p->agg.pCurrent->aMem[i]; - ctx.s.z = pMem->zShort; /* Space used for small aggregate contexts */ - ctx.pAgg = pMem->z; - ctx.cnt = ++pMem->i; - ctx.isError = 0; - ctx.isStep = 1; - ctx.pColl = 0; - if( ctx.pFunc->needCollSeq ){ - assert( pOp>p->aOp ); - assert( pOp[-1].p3type==P3_COLLSEQ ); - assert( pOp[-1].opcode==OP_CollSeq ); - ctx.pColl = (CollSeq *)pOp[-1].p3; - } - (ctx.pFunc->xStep)(&ctx, n, apVal); - pMem->z = ctx.pAgg; - pMem->flags = MEM_AggCtx; - popStack(&pTos, n+1); - if( ctx.isError ){ - rc = SQLITE_ERROR; - } - break; -} - -/* Opcode: AggFocus * P2 * -** -** Pop the top of the stack and use that as an aggregator key. If -** an aggregator with that same key already exists, then make the -** aggregator the current aggregator and jump to P2. If no aggregator -** with the given key exists, create one and make it current but -** do not jump. -** -** The order of aggregator opcodes is important. The order is: -** AggReset AggFocus AggNext. In other words, you must execute -** AggReset first, then zero or more AggFocus operations, then -** zero or more AggNext operations. You must not execute an AggFocus -** in between an AggNext and an AggReset. -*/ -case OP_AggFocus: { - char *zKey; - int nKey; - int res; - assert( pTos>=p->aStack ); - Stringify(pTos, db->enc); - zKey = pTos->z; - nKey = pTos->n; - assert( p->agg.pBtree ); - assert( p->agg.pCsr ); - rc = sqlite3BtreeMoveto(p->agg.pCsr, zKey, nKey, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( res==0 ){ - rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), - (char *)&p->agg.pCurrent); - pc = pOp->p2 - 1; - }else{ - rc = AggInsert(&p->agg, zKey, nKey); - } - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - Release(pTos); - pTos--; - break; -} - -/* Opcode: AggSet * P2 * -** -** Move the top of the stack into the P2-th field of the current -** aggregate. String values are duplicated into new memory. -*/ -case OP_AggSet: { - AggElem *pFocus; - int i = pOp->p2; - pFocus = p->agg.pCurrent; - assert( pTos>=p->aStack ); - if( pFocus==0 ) goto no_mem; - assert( i>=0 && i<p->agg.nMem ); - rc = sqlite3VdbeMemMove(&pFocus->aMem[i], pTos); - pTos--; - break; -} - -/* Opcode: AggGet * P2 * -** -** Push a new entry onto the stack which is a copy of the P2-th field -** of the current aggregate. Strings are not duplicated so -** string values will be ephemeral. -*/ -case OP_AggGet: { - AggElem *pFocus; - int i = pOp->p2; - pFocus = p->agg.pCurrent; - if( pFocus==0 ) goto no_mem; - assert( i>=0 && i<p->agg.nMem ); - pTos++; - sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem); - if( pTos->flags&MEM_Str ){ - sqlite3VdbeChangeEncoding(pTos, db->enc); - } - break; -} - -/* Opcode: AggNext * P2 * -** -** Make the next aggregate value the current aggregate. The prior -** aggregate is deleted. If all aggregate values have been consumed, -** jump to P2. -** -** The order of aggregator opcodes is important. The order is: -** AggReset AggFocus AggNext. In other words, you must execute -** AggReset first, then zero or more AggFocus operations, then -** zero or more AggNext operations. You must not execute an AggFocus -** in between an AggNext and an AggReset. -*/ -case OP_AggNext: { - int res; - assert( rc==SQLITE_OK ); - CHECK_FOR_INTERRUPT; - if( p->agg.searching==0 ){ - p->agg.searching = 1; - if( p->agg.pCsr ){ - rc = sqlite3BtreeFirst(p->agg.pCsr, &res); - }else{ - res = 0; - } - }else{ - if( p->agg.pCsr ){ - rc = sqlite3BtreeNext(p->agg.pCsr, &res); - }else{ - res = 1; - } - } - if( rc!=SQLITE_OK ) goto abort_due_to_error; - if( res!=0 ){ - pc = pOp->p2 - 1; - }else{ - int i; - sqlite3_context ctx; - Mem *aMem; - - if( p->agg.pCsr ){ - rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), - (char *)&p->agg.pCurrent); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - aMem = p->agg.pCurrent->aMem; - for(i=0; i<p->agg.nMem; i++){ - FuncDef *pFunc = p->agg.apFunc[i]; - Mem *pMem = &aMem[i]; - if( pFunc==0 || pFunc->xFinalize==0 ) continue; - ctx.s.flags = MEM_Null; - ctx.s.z = pMem->zShort; - ctx.pAgg = (void*)pMem->z; - ctx.cnt = pMem->i; - ctx.isStep = 0; - ctx.pFunc = pFunc; - pFunc->xFinalize(&ctx); - pMem->z = ctx.pAgg; - if( pMem->z && pMem->z!=pMem->zShort ){ - sqliteFree( pMem->z ); - } - *pMem = ctx.s; - if( pMem->flags & MEM_Short ){ - pMem->z = pMem->zShort; - } - } - } - break; -} - -/* Opcode: Vacuum * * * -** -** Vacuum the entire database. This opcode will cause other virtual -** machines to be created and run. It may not be called from within -** a transaction. -*/ -case OP_Vacuum: { - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - rc = sqlite3RunVacuum(&p->zErrMsg, db); - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; - break; -} - -/* An other opcode is illegal... -*/ -default: { - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",pOp->opcode); - sqlite3SetString(&p->zErrMsg, "unknown opcode ", zBuf, (char*)0); - rc = SQLITE_INTERNAL; - break; -} - -/***************************************************************************** -** The cases of the switch statement above this line should all be indented -** by 6 spaces. But the left-most 6 spaces have been removed to improve the -** readability. From this point on down, the normal indentation rules are -** restored. -*****************************************************************************/ - } - -#ifdef VDBE_PROFILE - { - long long elapse = hwtime() - start; - pOp->cycles += elapse; - pOp->cnt++; -#if 0 - fprintf(stdout, "%10lld ", elapse); - sqlite3VdbePrintOp(stdout, origPc, &p->aOp[origPc]); -#endif - } -#endif - - /* The following code adds nothing to the actual functionality - ** of the program. It is only here for testing and debugging. - ** On the other hand, it does burn CPU cycles every time through - ** the evaluator loop. So we can leave it out when NDEBUG is defined. - */ -#ifndef NDEBUG - /* Sanity checking on the top element of the stack */ - if( pTos>=p->aStack ){ - sqlite3VdbeMemSanity(pTos, db->enc); - } - if( pc<-1 || pc>=p->nOp ){ - sqlite3SetString(&p->zErrMsg, "jump destination out of range", (char*)0); - rc = SQLITE_INTERNAL; - } - if( p->trace && pTos>=p->aStack ){ - int i; - fprintf(p->trace, "Stack:"); - for(i=0; i>-5 && &pTos[i]>=p->aStack; i--){ - if( pTos[i].flags & MEM_Null ){ - fprintf(p->trace, " NULL"); - }else if( (pTos[i].flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - fprintf(p->trace, " si:%lld", pTos[i].i); - }else if( pTos[i].flags & MEM_Int ){ - fprintf(p->trace, " i:%lld", pTos[i].i); - }else if( pTos[i].flags & MEM_Real ){ - fprintf(p->trace, " r:%g", pTos[i].r); - }else{ - char zBuf[100]; - sqlite3VdbeMemPrettyPrint(&pTos[i], zBuf, 100); - fprintf(p->trace, " "); - fprintf(p->trace, "%s", zBuf); - } - } - if( rc!=0 ) fprintf(p->trace," rc=%d",rc); - fprintf(p->trace,"\n"); - } -#endif - } /* The end of the for(;;) loop the loops through opcodes */ - - /* If we reach this point, it means that execution is finished. - */ -vdbe_halt: - if( rc ){ - p->rc = rc; - rc = SQLITE_ERROR; - }else{ - rc = SQLITE_DONE; - } - sqlite3VdbeHalt(p); - p->pTos = pTos; - return rc; - - /* Jump to here if a malloc() fails. It's hard to get a malloc() - ** to fail on a modern VM computer, so this code is untested. - */ -no_mem: - sqlite3SetString(&p->zErrMsg, "out of memory", (char*)0); - rc = SQLITE_NOMEM; - goto vdbe_halt; - - /* Jump to here for an SQLITE_MISUSE error. - */ -abort_due_to_misuse: - rc = SQLITE_MISUSE; - /* Fall thru into abort_due_to_error */ - - /* Jump to here for any other kind of fatal error. The "rc" variable - ** should hold the error number. - */ -abort_due_to_error: - if( p->zErrMsg==0 ){ - if( sqlite3_malloc_failed ) rc = SQLITE_NOMEM; - sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0); - } - goto vdbe_halt; - - /* Jump to here if the sqlite3_interrupt() API sets the interrupt - ** flag. - */ -abort_due_to_interrupt: - assert( db->flags & SQLITE_Interrupt ); - db->flags &= ~SQLITE_Interrupt; - if( db->magic!=SQLITE_MAGIC_BUSY ){ - rc = SQLITE_MISUSE; - }else{ - rc = SQLITE_INTERRUPT; - } - p->rc = rc; - sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0); - goto vdbe_halt; -} diff --git a/kopete/plugins/statistics/sqlite/vdbe.h b/kopete/plugins/statistics/sqlite/vdbe.h deleted file mode 100644 index 490417a4..00000000 --- a/kopete/plugins/statistics/sqlite/vdbe.h +++ /dev/null @@ -1,131 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Header file for the Virtual DataBase Engine (VDBE) -** -** This header defines the interface to the virtual database engine -** or VDBE. The VDBE implements an abstract machine that runs a -** simple program to access and modify the underlying database. -** -** $Id$ -*/ -#ifndef _SQLITE_VDBE_H_ -#define _SQLITE_VDBE_H_ -#include <stdio.h> - -/* -** A single VDBE is an opaque structure named "Vdbe". Only routines -** in the source file sqliteVdbe.c are allowed to see the insides -** of this structure. -*/ -typedef struct Vdbe Vdbe; - -/* -** A single instruction of the virtual machine has an opcode -** and as many as three operands. The instruction is recorded -** as an instance of the following structure: -*/ -struct VdbeOp { - u8 opcode; /* What operation to perform */ - int p1; /* First operand */ - int p2; /* Second parameter (often the jump destination) */ - char *p3; /* Third parameter */ - int p3type; /* P3_STATIC, P3_DYNAMIC or P3_POINTER */ -#ifdef VDBE_PROFILE - int cnt; /* Number of times this instruction was executed */ - long long cycles; /* Total time spend executing this instruction */ -#endif -}; -typedef struct VdbeOp VdbeOp; - -/* -** A smaller version of VdbeOp used for the VdbeAddOpList() function because -** it takes up less space. -*/ -struct VdbeOpList { - u8 opcode; /* What operation to perform */ - signed char p1; /* First operand */ - short int p2; /* Second parameter (often the jump destination) */ - char *p3; /* Third parameter */ -}; -typedef struct VdbeOpList VdbeOpList; - -/* -** Allowed values of VdbeOp.p3type -*/ -#define P3_NOTUSED 0 /* The P3 parameter is not used */ -#define P3_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ -#define P3_STATIC (-2) /* Pointer to a static string */ -#define P3_POINTER (-3) /* P3 is a pointer to some structure or object */ -#define P3_COLLSEQ (-4) /* P3 is a pointer to a CollSeq structure */ -#define P3_FUNCDEF (-5) /* P3 is a pointer to a FuncDef structure */ -#define P3_KEYINFO (-6) /* P3 is a pointer to a KeyInfo structure */ -#define P3_VDBEFUNC (-7) /* P3 is a pointer to a VdbeFunc structure */ - -/* When adding a P3 argument using P3_KEYINFO, a copy of the KeyInfo structure -** is made. That copy is freed when the Vdbe is finalized. But if the -** argument is P3_KEYINFO_HANDOFF, the passed in pointer is used. It still -** gets freed when the Vdbe is finalized so it still should be obtained -** from a single sqliteMalloc(). But no copy is made and the calling -** function should *not* try to free the KeyInfo. -*/ -#define P3_KEYINFO_HANDOFF (-7) - -/* -** The following macro converts a relative address in the p2 field -** of a VdbeOp structure into a negative number so that -** sqlite3VdbeAddOpList() knows that the address is relative. Calling -** the macro again restores the address. -*/ -#define ADDR(X) (-1-(X)) - -/* -** The makefile scans the vdbe.c source file and creates the "opcodes.h" -** header file that defines a number for each opcode used by the VDBE. -*/ -#include "opcodes.h" - -/* -** Prototypes for the VDBE interface. See comments on the implementation -** for a description of what each of these routines does. -*/ -Vdbe *sqlite3VdbeCreate(sqlite3*); -void sqlite3VdbeCreateCallback(Vdbe*, int*); -int sqlite3VdbeAddOp(Vdbe*,int,int,int); -int sqlite3VdbeOp3(Vdbe*,int,int,int,const char *zP3,int); -int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); -void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); -void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); -void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); -void sqlite3VdbeDequoteP3(Vdbe*, int addr); -int sqlite3VdbeFindOp(Vdbe*, int, int, int); -VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); -int sqlite3VdbeMakeLabel(Vdbe*); -void sqlite3VdbeDelete(Vdbe*); -void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); -int sqlite3VdbeFinalize(Vdbe*); -void sqlite3VdbeResolveLabel(Vdbe*, int); -int sqlite3VdbeCurrentAddr(Vdbe*); -void sqlite3VdbeTrace(Vdbe*,FILE*); -int sqlite3VdbeReset(Vdbe*); -int sqliteVdbeSetVariables(Vdbe*,int,const char**); -void sqlite3VdbeSetNumCols(Vdbe*,int); -int sqlite3VdbeSetColName(Vdbe*, int, const char *, int); -void sqlite3VdbeCountChanges(Vdbe*); - -#ifndef NDEBUG - void sqlite3VdbeComment(Vdbe*, const char*, ...); -# define VdbeComment(X) sqlite3VdbeComment X -#else -# define VdbeComment(X) -#endif - -#endif diff --git a/kopete/plugins/statistics/sqlite/vdbeInt.h b/kopete/plugins/statistics/sqlite/vdbeInt.h deleted file mode 100644 index a929cb95..00000000 --- a/kopete/plugins/statistics/sqlite/vdbeInt.h +++ /dev/null @@ -1,408 +0,0 @@ -/* -** 2003 September 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the header file for information that is private to the -** VDBE. This information used to all be at the top of the single -** source code file "vdbe.c". When that file became too big (over -** 6000 lines long) it was split up into several smaller files and -** this header information was factored out. -*/ - -/* -** intToKey() and keyToInt() used to transform the rowid. But with -** the latest versions of the design they are no-ops. -*/ -#define keyToInt(X) (X) -#define intToKey(X) (X) - -/* -** The makefile scans the vdbe.c source file and creates the following -** array of string constants which are the names of all VDBE opcodes. This -** array is defined in a separate source code file named opcode.c which is -** automatically generated by the makefile. -*/ -extern char *sqlite3OpcodeNames[]; - -/* -** SQL is translated into a sequence of instructions to be -** executed by a virtual machine. Each instruction is an instance -** of the following structure. -*/ -typedef struct VdbeOp Op; - -/* -** Boolean values -*/ -typedef unsigned char Bool; - -/* -** A cursor is a pointer into a single BTree within a database file. -** The cursor can seek to a BTree entry with a particular key, or -** loop over all entries of the Btree. You can also insert new BTree -** entries or retrieve the key or data from the entry that the cursor -** is currently pointing to. -** -** Every cursor that the virtual machine has open is represented by an -** instance of the following structure. -** -** If the Cursor.isTriggerRow flag is set it means that this cursor is -** really a single row that represents the NEW or OLD pseudo-table of -** a row trigger. The data for the row is stored in Cursor.pData and -** the rowid is in Cursor.iKey. -*/ -struct Cursor { - BtCursor *pCursor; /* The cursor structure of the backend */ - i64 lastRecno; /* Last recno from a Next or NextIdx operation */ - i64 nextRowid; /* Next rowid returned by OP_NewRowid */ - Bool zeroed; /* True if zeroed out and ready for reuse */ - Bool recnoIsValid; /* True if lastRecno is valid */ - Bool keyAsData; /* The OP_Column command works on key instead of data */ - Bool atFirst; /* True if pointing to first entry */ - Bool useRandomRowid; /* Generate new record numbers semi-randomly */ - Bool nullRow; /* True if pointing to a row with no data */ - Bool nextRowidValid; /* True if the nextRowid field is valid */ - Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */ - Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ - Bool intKey; /* True if the table requires integer keys */ - Bool zeroData; /* True if table contains keys only - no data */ - u8 bogusIncrKey; /* Something for pIncrKey to point to if pKeyInfo==0 */ - i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - Btree *pBt; /* Separate file holding temporary table */ - int nData; /* Number of bytes in pData */ - char *pData; /* Data for a NEW or OLD pseudo-table */ - i64 iKey; /* Key for the NEW or OLD pseudo-table row */ - u8 *pIncrKey; /* Pointer to pKeyInfo->incrKey */ - KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - int nField; /* Number of fields in the header */ - - /* Cached information about the header for the data record that the - ** cursor is currently pointing to. Only valid if cacheValid is true. - ** zRow might point to (ephemeral) data for the current row, or it might - ** be NULL. */ - Bool cacheValid; /* True if the cache is valid */ - int payloadSize; /* Total number of bytes in the record */ - u32 *aType; /* Type values for all entries in the record */ - u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ -}; -typedef struct Cursor Cursor; - -/* -** Number of bytes of string storage space available to each stack -** layer without having to malloc. NBFS is short for Number of Bytes -** For Strings. -*/ -#define NBFS 32 - -/* -** Internally, the vdbe manipulates nearly all SQL values as Mem -** structures. Each Mem struct may cache multiple representations (string, -** integer etc.) of the same value. A value (and therefore Mem structure) -** has the following properties: -** -** Each value has a manifest type. The manifest type of the value stored -** in a Mem struct is returned by the MemType(Mem*) macro. The type is -** one of SQLITE_NULL, SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT or -** SQLITE_BLOB. -*/ -struct Mem { - i64 i; /* Integer value */ - int n; /* Number of characters in string value, including '\0' */ - u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ - u8 type; /* One of MEM_Null, MEM_Str, etc. */ - u8 enc; /* TEXT_Utf8, TEXT_Utf16le, or TEXT_Utf16be */ - double r; /* Real value */ - char *z; /* String or BLOB value */ - void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ - char zShort[NBFS]; /* Space for short strings */ -}; -typedef struct Mem Mem; - -/* -** A sorter builds a list of elements to be sorted. Each element of -** the list is an instance of the following structure. -*/ -typedef struct Sorter Sorter; -struct Sorter { - int nKey; /* Number of bytes in the key */ - char *zKey; /* The key by which we will sort */ - Mem data; - Sorter *pNext; /* Next in the list */ -}; - -/* -** Number of buckets used for merge-sort. -*/ -#define NSORT 30 - -/* One or more of the following flags are set to indicate the validOK -** representations of the value stored in the Mem struct. -** -** If the MEM_Null flag is set, then the value is an SQL NULL value. -** No other flags may be set in this case. -** -** If the MEM_Str flag is set then Mem.z points at a string representation. -** Usually this is encoded in the same unicode encoding as the main -** database (see below for exceptions). If the MEM_Term flag is also -** set, then the string is nul terminated. The MEM_Int and MEM_Real -** flags may coexist with the MEM_Str flag. -** -** Multiple of these values can appear in Mem.flags. But only one -** at a time can appear in Mem.type. -*/ -#define MEM_Null 0x0001 /* Value is NULL */ -#define MEM_Str 0x0002 /* Value is a string */ -#define MEM_Int 0x0004 /* Value is an integer */ -#define MEM_Real 0x0008 /* Value is a real number */ -#define MEM_Blob 0x0010 /* Value is a BLOB */ - -/* Whenever Mem contains a valid string or blob representation, one of -** the following flags must be set to determine the memory management -** policy for Mem.z. The MEM_Term flag tells us whether or not the -** string is \000 or \u0000 terminated -*/ -#define MEM_Term 0x0020 /* String rep is nul terminated */ -#define MEM_Dyn 0x0040 /* Need to call sqliteFree() on Mem.z */ -#define MEM_Static 0x0080 /* Mem.z points to a static string */ -#define MEM_Ephem 0x0100 /* Mem.z points to an ephemeral string */ -#define MEM_Short 0x0200 /* Mem.z points to Mem.zShort */ - -/* The following MEM_ value appears only in AggElem.aMem.s.flag fields. -** It indicates that the corresponding AggElem.aMem.z points to a -** aggregate function context that needs to be finalized. -*/ -#define MEM_AggCtx 0x0400 /* Mem.z points to an agg function context */ - - -/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains -** additional information about auxiliary information bound to arguments -** of the function. This is used to implement the sqlite3_get_auxdata() -** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data -** that can be associated with a constant argument to a function. This -** allows functions such as "regexp" to compile their constant regular -** expression argument once and reused the compiled code for multiple -** invocations. -*/ -struct VdbeFunc { - FuncDef *pFunc; /* The definition of the function */ - int nAux; /* Number of entries allocated for apAux[] */ - struct AuxData { - void *pAux; /* Aux data for the i-th argument */ - void (*xDelete)(void *); /* Destructor for the aux data */ - } apAux[1]; /* One slot for each function argument */ -}; -typedef struct VdbeFunc VdbeFunc; - -/* -** The "context" argument for a installable function. A pointer to an -** instance of this structure is the first argument to the routines used -** implement the SQL functions. -** -** There is a typedef for this structure in sqlite.h. So all routines, -** even the public interface to SQLite, can use a pointer to this structure. -** But this file is the only place where the internal details of this -** structure are known. -** -** This structure is defined inside of vdbe.c because it uses substructures -** (Mem) which are only defined there. -*/ -struct sqlite3_context { - FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */ - VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */ - Mem s; /* The return value is stored here */ - void *pAgg; /* Aggregate context */ - u8 isError; /* Set to true for an error */ - u8 isStep; /* Current in the step function */ - int cnt; /* Number of times that the step function has been called */ - CollSeq *pColl; -}; - -/* -** An Agg structure describes an Aggregator. Each Agg consists of -** zero or more Aggregator elements (AggElem). Each AggElem contains -** a key and one or more values. The values are used in processing -** aggregate functions in a SELECT. The key is used to implement -** the GROUP BY clause of a select. -*/ -typedef struct Agg Agg; -typedef struct AggElem AggElem; -struct Agg { - int nMem; /* Number of values stored in each AggElem */ - AggElem *pCurrent; /* The AggElem currently in focus */ - FuncDef **apFunc; /* Information about aggregate functions */ - Btree *pBtree; /* The tmp. btree used to group elements, if required. */ - BtCursor *pCsr; /* Read/write cursor to the table in pBtree */ - int nTab; /* Root page of the table in pBtree */ - u8 searching; /* True between the first AggNext and AggReset */ -}; -struct AggElem { - char *zKey; /* The key to this AggElem */ - int nKey; /* Number of bytes in the key, including '\0' at end */ - Mem aMem[1]; /* The values for this AggElem */ -}; - -/* -** A Set structure is used for quick testing to see if a value -** is part of a small set. Sets are used to implement code like -** this: -** x.y IN ('hi','hoo','hum') -*/ -typedef struct Set Set; -struct Set { - Hash hash; /* A set is just a hash table */ - HashElem *prev; /* Previously accessed hash elemen */ -}; - -/* -** A Keylist is a bunch of keys into a table. The keylist can -** grow without bound. The keylist stores the ROWIDs of database -** records that need to be deleted or updated. -*/ -typedef struct Keylist Keylist; -struct Keylist { - int nKey; /* Number of slots in aKey[] */ - int nUsed; /* Next unwritten slot in aKey[] */ - int nRead; /* Next unread slot in aKey[] */ - Keylist *pNext; /* Next block of keys */ - i64 aKey[1]; /* One or more keys. Extra space allocated as needed */ -}; - -/* -** A Context stores the last insert rowid, the last statement change count, -** and the current statement change count (i.e. changes since last statement). -** The current keylist is also stored in the context. -** Elements of Context structure type make up the ContextStack, which is -** updated by the ContextPush and ContextPop opcodes (used by triggers). -** The context is pushed before executing a trigger a popped when the -** trigger finishes. -*/ -typedef struct Context Context; -struct Context { - int lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ - int nChange; /* Statement changes (Vdbe.nChanges) */ - Keylist *pList; /* Records that will participate in a DELETE or UPDATE */ -}; - -/* -** An instance of the virtual machine. This structure contains the complete -** state of the virtual machine. -** -** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile() -** is really a pointer to an instance of this structure. -*/ -struct Vdbe { - sqlite3 *db; /* The whole database */ - Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ - FILE *trace; /* Write an execution trace here, if not NULL */ - int nOp; /* Number of instructions in the program */ - int nOpAlloc; /* Number of slots allocated for aOp[] */ - Op *aOp; /* Space to hold the virtual machine's program */ - int nLabel; /* Number of labels used */ - int nLabelAlloc; /* Number of slots allocated in aLabel[] */ - int *aLabel; /* Space to hold the labels */ - Mem *aStack; /* The operand stack, except string values */ - Mem *pTos; /* Top entry in the operand stack */ - Mem **apArg; /* Arguments to currently executing user function */ - Mem *aColName; /* Column names to return */ - int nCursor; /* Number of slots in apCsr[] */ - Cursor **apCsr; /* One element of this array for each open cursor */ - Sorter *pSort; /* A linked list of objects to be sorted */ - int nVar; /* Number of entries in aVar[] */ - Mem *aVar; /* Values for the OP_Variable opcode. */ - char **azVar; /* Name of variables */ - int okVar; /* True if azVar[] has been initialized */ - int magic; /* Magic number for sanity checking */ - int nMem; /* Number of memory locations currently allocated */ - Mem *aMem; /* The memory locations */ - Agg agg; /* Aggregate information */ - int nCallback; /* Number of callbacks invoked so far */ - Keylist *pList; /* A list of ROWIDs */ - int contextStackTop; /* Index of top element in the context stack */ - int contextStackDepth; /* The size of the "context" stack */ - Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/ - int pc; /* The program counter */ - int rc; /* Value to return */ - unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */ - int errorAction; /* Recovery action to do in case of an error */ - int inTempTrans; /* True if temp database is transactioned */ - int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */ - int returnDepth; /* Next unused element in returnStack[] */ - int nResColumn; /* Number of columns in one row of the result set */ - char **azResColumn; /* Values for one row of result */ - int popStack; /* Pop the stack this much on entry to VdbeExec() */ - char *zErrMsg; /* Error message written here */ - u8 resOnStack; /* True if there are result values on the stack */ - u8 explain; /* True if EXPLAIN present on SQL command */ - u8 changeCntOn; /* True to update the change-counter */ - u8 aborted; /* True if ROLLBACK in another VM causes an abort */ - int nChange; /* Number of db changes made since last reset */ -}; - -/* -** The following are allowed values for Vdbe.magic -*/ -#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */ -#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */ -#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */ -#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */ - -/* -** Function prototypes -*/ -void sqlite3VdbeFreeCursor(Cursor*); -void sqlite3VdbeSorterReset(Vdbe*); -int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *); -void sqlite3VdbeKeylistFree(Keylist*); -void sqliteVdbePopStack(Vdbe*,int); -int sqlite3VdbeCursorMoveto(Cursor*); -#if !defined(NDEBUG) || defined(VDBE_PROFILE) -void sqlite3VdbePrintOp(FILE*, int, Op*); -#endif -void sqlite3VdbePrintSql(Vdbe*); -int sqlite3VdbeSerialTypeLen(u32); -u32 sqlite3VdbeSerialType(Mem*); -int sqlite3VdbeSerialPut(unsigned char*, Mem*); -int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); -void sqlite3VdbeDeleteAuxData(VdbeFunc*, int); - -int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); -int sqlite3VdbeIdxKeyCompare(Cursor*, int , const unsigned char*, int*); -int sqlite3VdbeIdxRowid(BtCursor *, i64 *); -int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); -int sqlite3VdbeRecordCompare(void*,int,const void*,int, const void*); -int sqlite3VdbeIdxRowidLen(int,const u8*); -int sqlite3VdbeExec(Vdbe*); -int sqlite3VdbeList(Vdbe*); -int sqlite3VdbeHalt(Vdbe*); -int sqlite3VdbeChangeEncoding(Mem *, int); -int sqlite3VdbeMemCopy(Mem*, const Mem*); -void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); -int sqlite3VdbeMemMove(Mem*, Mem*); -int sqlite3VdbeMemNulTerminate(Mem*); -int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); -void sqlite3VdbeMemSetInt64(Mem*, i64); -void sqlite3VdbeMemSetDouble(Mem*, double); -void sqlite3VdbeMemSetNull(Mem*); -int sqlite3VdbeMemMakeWriteable(Mem*); -int sqlite3VdbeMemDynamicify(Mem*); -int sqlite3VdbeMemStringify(Mem*, int); -i64 sqlite3VdbeIntValue(Mem*); -int sqlite3VdbeMemIntegerify(Mem*); -double sqlite3VdbeRealValue(Mem*); -int sqlite3VdbeMemRealify(Mem*); -int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); -void sqlite3VdbeMemRelease(Mem *p); -#ifndef NDEBUG -void sqlite3VdbeMemSanity(Mem*, u8); -#endif -int sqlite3VdbeMemTranslate(Mem*, u8); -void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf); -int sqlite3VdbeMemHandleBom(Mem *pMem); diff --git a/kopete/plugins/statistics/sqlite/vdbeapi.c b/kopete/plugins/statistics/sqlite/vdbeapi.c deleted file mode 100644 index f6047f6f..00000000 --- a/kopete/plugins/statistics/sqlite/vdbeapi.c +++ /dev/null @@ -1,588 +0,0 @@ -/* -** 2004 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to implement APIs that are part of the -** VDBE. -*/ -#include "sqliteInt.h" -#include "vdbeInt.h" - -/**************************** sqlite3_value_ ******************************* -** The following routines extract information from a Mem or sqlite3_value -** structure. -*/ -const void *sqlite3_value_blob(sqlite3_value *pVal){ - Mem *p = (Mem*)pVal; - if( p->flags & (MEM_Blob|MEM_Str) ){ - return p->z; - }else{ - return sqlite3_value_text(pVal); - } -} -int sqlite3_value_bytes(sqlite3_value *pVal){ - return sqlite3ValueBytes(pVal, SQLITE_UTF8); -} -int sqlite3_value_bytes16(sqlite3_value *pVal){ - return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); -} -double sqlite3_value_double(sqlite3_value *pVal){ - return sqlite3VdbeRealValue((Mem*)pVal); -} -int sqlite3_value_int(sqlite3_value *pVal){ - return sqlite3VdbeIntValue((Mem*)pVal); -} -sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ - return sqlite3VdbeIntValue((Mem*)pVal); -} -const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ - return (const char *)sqlite3ValueText(pVal, SQLITE_UTF8); -} -const void *sqlite3_value_text16(sqlite3_value* pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); -} -const void *sqlite3_value_text16be(sqlite3_value *pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16BE); -} -const void *sqlite3_value_text16le(sqlite3_value *pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16LE); -} -int sqlite3_value_type(sqlite3_value* pVal){ - return pVal->type; -} - -/**************************** sqlite3_result_ ******************************* -** The following routines are used by user-defined functions to specify -** the function result. -*/ -void sqlite3_result_blob( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( n>0 ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, 0, xDel); -} -void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ - sqlite3VdbeMemSetDouble(&pCtx->s, rVal); -} -void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ - pCtx->isError = 1; - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); -} -void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ - pCtx->isError = 1; - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); -} -void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ - sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal); -} -void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ - sqlite3VdbeMemSetInt64(&pCtx->s, iVal); -} -void sqlite3_result_null(sqlite3_context *pCtx){ - sqlite3VdbeMemSetNull(&pCtx->s); -} -void sqlite3_result_text( - sqlite3_context *pCtx, - const char *z, - int n, - void (*xDel)(void *) -){ - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, xDel); -} -void sqlite3_result_text16( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, xDel); -} -void sqlite3_result_text16be( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16BE, xDel); -} -void sqlite3_result_text16le( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16LE, xDel); -} -void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ - sqlite3VdbeMemCopy(&pCtx->s, pValue); -} - - -/* -** Execute the statement pStmt, either until a row of data is ready, the -** statement is completely executed or an error occurs. -*/ -int sqlite3_step(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - sqlite3 *db; - int rc; - - if( p==0 || p->magic!=VDBE_MAGIC_RUN ){ - return SQLITE_MISUSE; - } - if( p->aborted ){ - return SQLITE_ABORT; - } - db = p->db; - if( sqlite3SafetyOn(db) ){ - p->rc = SQLITE_MISUSE; - return SQLITE_MISUSE; - } - if( p->pc<0 ){ - /* Invoke the trace callback if there is one - */ - if( (db = p->db)->xTrace && !db->init.busy ){ - assert( p->nOp>0 ); - assert( p->aOp[p->nOp-1].opcode==OP_Noop ); - assert( p->aOp[p->nOp-1].p3!=0 ); - assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC ); - sqlite3SafetyOff(db); - db->xTrace(db->pTraceArg, p->aOp[p->nOp-1].p3); - if( sqlite3SafetyOn(db) ){ - p->rc = SQLITE_MISUSE; - return SQLITE_MISUSE; - } - } - - /* Print a copy of SQL as it is executed if the SQL_TRACE pragma is turned - ** on in debugging mode. - */ -#ifdef SQLITE_DEBUG - if( (db->flags & SQLITE_SqlTrace)!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", p->aOp[p->nOp-1].p3); - } -#endif /* SQLITE_DEBUG */ - - db->activeVdbeCnt++; - p->pc = 0; - } - if( p->explain ){ - rc = sqlite3VdbeList(p); - }else{ - rc = sqlite3VdbeExec(p); - } - - if( sqlite3SafetyOff(db) ){ - rc = SQLITE_MISUSE; - } - - sqlite3Error(p->db, rc, p->zErrMsg); - return rc; -} - -/* -** Extract the user data from a sqlite3_context structure and return a -** pointer to it. -*/ -void *sqlite3_user_data(sqlite3_context *p){ - assert( p && p->pFunc ); - return p->pFunc->pUserData; -} - -/* -** Allocate or return the aggregate context for a user function. A new -** context is allocated on the first call. Subsequent calls return the -** same context that was returned on prior calls. -** -** This routine is defined here in vdbe.c because it depends on knowing -** the internals of the sqlite3_context structure which is only defined in -** this source file. -*/ -void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ - assert( p && p->pFunc && p->pFunc->xStep ); - if( p->pAgg==0 ){ - if( nByte<=NBFS ){ - p->pAgg = (void*)p->s.z; - memset(p->pAgg, 0, nByte); - }else{ - p->pAgg = sqliteMalloc( nByte ); - } - } - return p->pAgg; -} - -/* -** Return the auxilary data pointer, if any, for the iArg'th argument to -** the user-function defined by pCtx. -*/ -void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ - VdbeFunc *pVdbeFunc = pCtx->pVdbeFunc; - if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){ - return 0; - } - return pVdbeFunc->apAux[iArg].pAux; -} - -/* -** Set the auxilary data pointer and delete function, for the iArg'th -** argument to the user-function defined by pCtx. Any previous value is -** deleted by calling the delete function specified when it was set. -*/ -void sqlite3_set_auxdata( - sqlite3_context *pCtx, - int iArg, - void *pAux, - void (*xDelete)(void*) -){ - struct AuxData *pAuxData; - VdbeFunc *pVdbeFunc; - if( iArg<0 ) return; - - pVdbeFunc = pCtx->pVdbeFunc; - if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){ - int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg; - pCtx->pVdbeFunc = pVdbeFunc = sqliteRealloc(pVdbeFunc, nMalloc); - if( !pVdbeFunc ) return; - memset(&pVdbeFunc->apAux[pVdbeFunc->nAux], 0, - sizeof(struct AuxData)*(iArg+1-pVdbeFunc->nAux)); - pVdbeFunc->nAux = iArg+1; - pVdbeFunc->pFunc = pCtx->pFunc; - } - - pAuxData = &pVdbeFunc->apAux[iArg]; - if( pAuxData->pAux && pAuxData->xDelete ){ - pAuxData->xDelete(pAuxData->pAux); - } - pAuxData->pAux = pAux; - pAuxData->xDelete = xDelete; -} - -/* -** Return the number of times the Step function of a aggregate has been -** called. -** -** This routine is defined here in vdbe.c because it depends on knowing -** the internals of the sqlite3_context structure which is only defined in -** this source file. -*/ -int sqlite3_aggregate_count(sqlite3_context *p){ - assert( p && p->pFunc && p->pFunc->xStep ); - return p->cnt; -} - -/* -** Return the number of columns in the result set for the statement pStmt. -*/ -int sqlite3_column_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - return pVm ? pVm->nResColumn : 0; -} - -/* -** Return the number of values available from the current row of the -** currently executing statement pStmt. -*/ -int sqlite3_data_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - if( pVm==0 || !pVm->resOnStack ) return 0; - return pVm->nResColumn; -} - - -/* -** Check to see if column iCol of the given statement is valid. If -** it is, return a pointer to the Mem for the value of that column. -** If iCol is not valid, return a pointer to a Mem which has a value -** of NULL. -*/ -static Mem *columnMem(sqlite3_stmt *pStmt, int i){ - Vdbe *pVm = (Vdbe *)pStmt; - int vals = sqlite3_data_count(pStmt); - if( i>=vals || i<0 ){ - static Mem nullMem; - if( nullMem.flags==0 ){ nullMem.flags = MEM_Null; } - sqlite3Error(pVm->db, SQLITE_RANGE, 0); - return &nullMem; - } - return &pVm->pTos[(1-vals)+i]; -} - -/**************************** sqlite3_column_ ******************************* -** The following routines are used to access elements of the current row -** in the result set. -*/ -const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_blob( columnMem(pStmt,i) ); -} -int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_bytes( columnMem(pStmt,i) ); -} -int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_bytes16( columnMem(pStmt,i) ); -} -double sqlite3_column_double(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_double( columnMem(pStmt,i) ); -} -int sqlite3_column_int(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_int( columnMem(pStmt,i) ); -} -sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_int64( columnMem(pStmt,i) ); -} -const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_text( columnMem(pStmt,i) ); -} -const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_text16( columnMem(pStmt,i) ); -} -int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_type( columnMem(pStmt,i) ); -} - -/* -** Convert the N-th element of pStmt->pColName[] into a string using -** xFunc() then return that string. If N is out of range, return 0. -** If useType is 1, then use the second set of N elements (the datatype -** names) instead of the first set. -*/ -static const void *columnName( - sqlite3_stmt *pStmt, - int N, - const void *(*xFunc)(Mem*), - int useType -){ - Vdbe *p = (Vdbe *)pStmt; - int n = sqlite3_column_count(pStmt); - - if( p==0 || N>=n || N<0 ){ - return 0; - } - if( useType ){ - N += n; - } - return xFunc(&p->aColName[N]); -} - - -/* -** Return the name of the Nth column of the result set returned by SQL -** statement pStmt. -*/ -const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, 0); -} - -/* -** Return the name of the 'i'th column of the result set of SQL statement -** pStmt, encoded as UTF-16. -*/ -const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, 0); -} - -/* -** Return the column declaration type (if applicable) of the 'i'th column -** of the result set of SQL statement pStmt, encoded as UTF-8. -*/ -const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, 1); -} - -/* -** Return the column declaration type (if applicable) of the 'i'th column -** of the result set of SQL statement pStmt, encoded as UTF-16. -*/ -const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, 1); -} - -/******************************* sqlite3_bind_ *************************** -** -** Routines used to attach values to wildcards in a compiled SQL statement. -*/ -/* -** Unbind the value bound to variable i in virtual machine p. This is the -** the same as binding a NULL value to the column. If the "i" parameter is -** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. -** -** The error code stored in database p->db is overwritten with the return -** value in any case. -*/ -static int vdbeUnbind(Vdbe *p, int i){ - Mem *pVar; - if( p==0 || p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ - sqlite3Error(p->db, SQLITE_MISUSE, 0); - return SQLITE_MISUSE; - } - if( i<1 || i>p->nVar ){ - sqlite3Error(p->db, SQLITE_RANGE, 0); - return SQLITE_RANGE; - } - i--; - pVar = &p->aVar[i]; - sqlite3VdbeMemRelease(pVar); - pVar->flags = MEM_Null; - sqlite3Error(p->db, SQLITE_OK, 0); - return SQLITE_OK; -} - -/* -** Bind a text or BLOB value. -*/ -static int bindText( - sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*), - int encoding -){ - Vdbe *p = (Vdbe *)pStmt; - Mem *pVar; - int rc; - - rc = vdbeUnbind(p, i); - if( rc || zData==0 ){ - return rc; - } - pVar = &p->aVar[i-1]; - rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); - if( rc ){ - return rc; - } - if( rc==SQLITE_OK && encoding!=0 ){ - rc = sqlite3VdbeChangeEncoding(pVar, p->db->enc); - } - return rc; -} - - -/* -** Bind a blob value to an SQL statement variable. -*/ -int sqlite3_bind_blob( - sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, 0); -} -int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); - } - return rc; -} -int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ - return sqlite3_bind_int64(p, i, (i64)iValue); -} -int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); - } - return rc; -} -int sqlite3_bind_null(sqlite3_stmt* p, int i){ - return vdbeUnbind((Vdbe *)p, i); -} -int sqlite3_bind_text( - sqlite3_stmt *pStmt, - int i, - const char *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); -} -int sqlite3_bind_text16( - sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); -} - -/* -** Return the number of wildcards that can be potentially bound to. -** This routine is added to support DBD::SQLite. -*/ -int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p ? p->nVar : 0; -} - -/* -** Create a mapping from variable numbers to variable names -** in the Vdbe.azVar[] array, if such a mapping does not already -** exist. -*/ -static void createVarMap(Vdbe *p){ - if( !p->okVar ){ - int j; - Op *pOp; - for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){ - if( pOp->opcode==OP_Variable ){ - assert( pOp->p1>0 && pOp->p1<=p->nVar ); - p->azVar[pOp->p1-1] = pOp->p3; - } - } - p->okVar = 1; - } -} - -/* -** Return the name of a wildcard parameter. Return NULL if the index -** is out of range or if the wildcard is unnamed. -** -** The result is always UTF-8. -*/ -const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ - Vdbe *p = (Vdbe*)pStmt; - if( p==0 || i<1 || i>p->nVar ){ - return 0; - } - createVarMap(p); - return p->azVar[i-1]; -} - -/* -** Given a wildcard parameter name, return the index of the variable -** with that name. If there is no variable with the given name, -** return 0. -*/ -int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ - Vdbe *p = (Vdbe*)pStmt; - int i; - if( p==0 ){ - return 0; - } - createVarMap(p); - for(i=0; i<p->nVar; i++){ - const char *z = p->azVar[i]; - if( z && strcmp(z,zName)==0 ){ - return i+1; - } - } - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/vdbeaux.c b/kopete/plugins/statistics/sqlite/vdbeaux.c deleted file mode 100644 index fa9751da..00000000 --- a/kopete/plugins/statistics/sqlite/vdbeaux.c +++ /dev/null @@ -1,1806 +0,0 @@ -/* -** 2003 September 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used for creating, destroying, and populating -** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior -** to version 2.8.7, all this code was combined into the vdbe.c source file. -** But that file was getting too big so this subroutines were split out. -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> -#include "vdbeInt.h" - - -/* -** When debugging the code generator in a symbolic debugger, one can -** set the sqlite3_vdbe_addop_trace to 1 and all opcodes will be printed -** as they are added to the instruction stream. -*/ -#ifndef NDEBUG -int sqlite3_vdbe_addop_trace = 0; -#endif - - -/* -** Create a new virtual database engine. -*/ -Vdbe *sqlite3VdbeCreate(sqlite3 *db){ - Vdbe *p; - p = sqliteMalloc( sizeof(Vdbe) ); - if( p==0 ) return 0; - p->db = db; - if( db->pVdbe ){ - db->pVdbe->pPrev = p; - } - p->pNext = db->pVdbe; - p->pPrev = 0; - db->pVdbe = p; - p->magic = VDBE_MAGIC_INIT; - return p; -} - -/* -** Turn tracing on or off -*/ -void sqlite3VdbeTrace(Vdbe *p, FILE *trace){ - p->trace = trace; -} - -/* -** Resize the Vdbe.aOp array so that it contains at least N -** elements. -*/ -static void resizeOpArray(Vdbe *p, int N){ - if( p->nOpAlloc<N ){ - int oldSize = p->nOpAlloc; - p->nOpAlloc = N+100; - p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); - if( p->aOp ){ - memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); - } - } -} - -/* -** Add a new instruction to the list of instructions current in the -** VDBE. Return the address of the new instruction. -** -** Parameters: -** -** p Pointer to the VDBE -** -** op The opcode for this instruction -** -** p1, p2 First two of the three possible operands. -** -** Use the sqlite3VdbeResolveLabel() function to fix an address and -** the sqlite3VdbeChangeP3() function to change the value of the P3 -** operand. -*/ -int sqlite3VdbeAddOp(Vdbe *p, int op, int p1, int p2){ - int i; - VdbeOp *pOp; - - i = p->nOp; - p->nOp++; - assert( p->magic==VDBE_MAGIC_INIT ); - resizeOpArray(p, i+1); - if( p->aOp==0 ){ - return 0; - } - pOp = &p->aOp[i]; - pOp->opcode = op; - pOp->p1 = p1; - pOp->p2 = p2; - pOp->p3 = 0; - pOp->p3type = P3_NOTUSED; -#ifndef NDEBUG - if( sqlite3_vdbe_addop_trace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); -#endif - return i; -} - -/* -** Add an opcode that includes the p3 value. -*/ -int sqlite3VdbeOp3(Vdbe *p, int op, int p1, int p2, const char *zP3,int p3type){ - int addr = sqlite3VdbeAddOp(p, op, p1, p2); - sqlite3VdbeChangeP3(p, addr, zP3, p3type); - return addr; -} - -/* -** Create a new symbolic label for an instruction that has yet to be -** coded. The symbolic label is really just a negative number. The -** label can be used as the P2 value of an operation. Later, when -** the label is resolved to a specific address, the VDBE will scan -** through its operation list and change all values of P2 which match -** the label into the resolved address. -** -** The VDBE knows that a P2 value is a label because labels are -** always negative and P2 values are suppose to be non-negative. -** Hence, a negative P2 value is a label that has yet to be resolved. -** -** Zero is returned if a malloc() fails. -*/ -int sqlite3VdbeMakeLabel(Vdbe *p){ - int i; - i = p->nLabel++; - assert( p->magic==VDBE_MAGIC_INIT ); - if( i>=p->nLabelAlloc ){ - p->nLabelAlloc = p->nLabelAlloc*2 + 10; - p->aLabel = sqliteRealloc( p->aLabel, p->nLabelAlloc*sizeof(p->aLabel[0])); - } - if( p->aLabel ){ - p->aLabel[i] = -1; - } - return -1-i; -} - -/* -** Resolve label "x" to be the address of the next instruction to -** be inserted. The parameter "x" must have been obtained from -** a prior call to sqlite3VdbeMakeLabel(). -*/ -void sqlite3VdbeResolveLabel(Vdbe *p, int x){ - int j = -1-x; - assert( p->magic==VDBE_MAGIC_INIT ); - assert( j>=0 && j<p->nLabel ); - if( p->aLabel ){ - p->aLabel[j] = p->nOp; - } -} - -/* -** Loop through the program looking for P2 values that are negative. -** Each such value is a label. Resolve the label by setting the P2 -** value to its correct non-zero value. -** -** This routine is called once after all opcodes have been inserted. -*/ -static void resolveP2Values(Vdbe *p){ - int i; - Op *pOp; - int *aLabel = p->aLabel; - if( aLabel==0 ) return; - for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ - if( pOp->p2>=0 ) continue; - assert( -1-pOp->p2<p->nLabel ); - pOp->p2 = aLabel[-1-pOp->p2]; - } - sqliteFree(p->aLabel); - p->aLabel = 0; -} - -/* -** Return the address of the next instruction to be inserted. -*/ -int sqlite3VdbeCurrentAddr(Vdbe *p){ - assert( p->magic==VDBE_MAGIC_INIT ); - return p->nOp; -} - -/* -** Add a whole list of operations to the operation stack. Return the -** address of the first operation added. -*/ -int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){ - int addr; - assert( p->magic==VDBE_MAGIC_INIT ); - resizeOpArray(p, p->nOp + nOp); - if( p->aOp==0 ){ - return 0; - } - addr = p->nOp; - if( nOp>0 ){ - int i; - VdbeOpList const *pIn = aOp; - for(i=0; i<nOp; i++, pIn++){ - int p2 = pIn->p2; - VdbeOp *pOut = &p->aOp[i+addr]; - pOut->opcode = pIn->opcode; - pOut->p1 = pIn->p1; - pOut->p2 = p2<0 ? addr + ADDR(p2) : p2; - pOut->p3 = pIn->p3; - pOut->p3type = pIn->p3 ? P3_STATIC : P3_NOTUSED; -#ifndef NDEBUG - if( sqlite3_vdbe_addop_trace ){ - sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]); - } -#endif - } - p->nOp += nOp; - } - return addr; -} - -/* -** Change the value of the P1 operand for a specific instruction. -** This routine is useful when a large program is loaded from a -** static array using sqlite3VdbeAddOpList but we want to make a -** few minor changes to the program. -*/ -void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ - assert( p->magic==VDBE_MAGIC_INIT ); - if( p && addr>=0 && p->nOp>addr && p->aOp ){ - p->aOp[addr].p1 = val; - } -} - -/* -** Change the value of the P2 operand for a specific instruction. -** This routine is useful for setting a jump destination. -*/ -void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ - assert( val>=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - if( p && addr>=0 && p->nOp>addr && p->aOp ){ - p->aOp[addr].p2 = val; - } -} - -/* -** Change the value of the P3 operand for a specific instruction. -** This routine is useful when a large program is loaded from a -** static array using sqlite3VdbeAddOpList but we want to make a -** few minor changes to the program. -** -** If n>=0 then the P3 operand is dynamic, meaning that a copy of -** the string is made into memory obtained from sqliteMalloc(). -** A value of n==0 means copy bytes of zP3 up to and including the -** first null byte. If n>0 then copy n+1 bytes of zP3. -** -** If n==P3_STATIC it means that zP3 is a pointer to a constant static -** string and we can just copy the pointer. n==P3_POINTER means zP3 is -** a pointer to some object other than a string. n==P3_COLLSEQ and -** n==P3_KEYINFO mean that zP3 is a pointer to a CollSeq or KeyInfo -** structure. A copy is made of KeyInfo structures into memory obtained -** from sqliteMalloc. -** -** If addr<0 then change P3 on the most recently inserted instruction. -*/ -void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){ - Op *pOp; - assert( p->magic==VDBE_MAGIC_INIT ); - if( p==0 || p->aOp==0 ) return; - if( addr<0 || addr>=p->nOp ){ - addr = p->nOp - 1; - if( addr<0 ) return; - } - pOp = &p->aOp[addr]; - if( pOp->p3 && pOp->p3type==P3_DYNAMIC ){ - sqliteFree(pOp->p3); - pOp->p3 = 0; - } - if( zP3==0 ){ - pOp->p3 = 0; - pOp->p3type = P3_NOTUSED; - }else if( n==P3_KEYINFO ){ - KeyInfo *pKeyInfo; - int nField, nByte; - nField = ((KeyInfo*)zP3)->nField; - nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]); - pKeyInfo = sqliteMallocRaw( nByte ); - pOp->p3 = (char*)pKeyInfo; - if( pKeyInfo ){ - memcpy(pKeyInfo, zP3, nByte); - pOp->p3type = P3_KEYINFO; - }else{ - pOp->p3type = P3_NOTUSED; - } - }else if( n==P3_KEYINFO_HANDOFF ){ - pOp->p3 = (char*)zP3; - pOp->p3type = P3_KEYINFO; - }else if( n<0 ){ - pOp->p3 = (char*)zP3; - pOp->p3type = n; - }else{ - if( n==0 ) n = strlen(zP3); - pOp->p3 = sqliteStrNDup(zP3, n); - pOp->p3type = P3_DYNAMIC; - } -} - -#ifndef NDEBUG -/* -** Replace the P3 field of the most recently coded instruction with -** comment text. -*/ -void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - assert( p->nOp>0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 ); - va_start(ap, zFormat); - sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC); - va_end(ap); -} -#endif - -/* -** If the P3 operand to the specified instruction appears -** to be a quoted string token, then this procedure removes -** the quotes. -** -** The quoting operator can be either a grave ascent (ASCII 0x27) -** or a double quote character (ASCII 0x22). Two quotes in a row -** resolve to be a single actual quote character within the string. -*/ -void sqlite3VdbeDequoteP3(Vdbe *p, int addr){ - Op *pOp; - assert( p->magic==VDBE_MAGIC_INIT ); - if( p->aOp==0 ) return; - if( addr<0 || addr>=p->nOp ){ - addr = p->nOp - 1; - if( addr<0 ) return; - } - pOp = &p->aOp[addr]; - if( pOp->p3==0 || pOp->p3[0]==0 ) return; - if( pOp->p3type==P3_STATIC ){ - pOp->p3 = sqliteStrDup(pOp->p3); - pOp->p3type = P3_DYNAMIC; - } - assert( pOp->p3type==P3_DYNAMIC ); - sqlite3Dequote(pOp->p3); -} - -/* -** Search the current program starting at instruction addr for the given -** opcode and P2 value. Return the address plus 1 if found and 0 if not -** found. -*/ -int sqlite3VdbeFindOp(Vdbe *p, int addr, int op, int p2){ - int i; - assert( p->magic==VDBE_MAGIC_INIT ); - for(i=addr; i<p->nOp; i++){ - if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return i+1; - } - return 0; -} - -/* -** Return the opcode for a given address. -*/ -VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ - assert( p->magic==VDBE_MAGIC_INIT ); - assert( addr>=0 && addr<p->nOp ); - return &p->aOp[addr]; -} - -/* -** Compute a string that describes the P3 parameter for an opcode. -** Use zTemp for any required temporary buffer space. -*/ -static char *displayP3(Op *pOp, char *zTemp, int nTemp){ - char *zP3; - assert( nTemp>=20 ); - switch( pOp->p3type ){ - case P3_POINTER: { - sprintf(zTemp, "ptr(%#x)", (int)pOp->p3); - zP3 = zTemp; - break; - } - case P3_KEYINFO: { - int i, j; - KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3; - sprintf(zTemp, "keyinfo(%d", pKeyInfo->nField); - i = strlen(zTemp); - for(j=0; j<pKeyInfo->nField; j++){ - CollSeq *pColl = pKeyInfo->aColl[j]; - if( pColl ){ - int n = strlen(pColl->zName); - if( i+n>nTemp-6 ){ - strcpy(&zTemp[i],",..."); - break; - } - zTemp[i++] = ','; - if( pKeyInfo->aSortOrder && pKeyInfo->aSortOrder[j] ){ - zTemp[i++] = '-'; - } - strcpy(&zTemp[i], pColl->zName); - i += n; - }else if( i+4<nTemp-6 ){ - strcpy(&zTemp[i],",nil"); - i += 4; - } - } - zTemp[i++] = ')'; - zTemp[i] = 0; - assert( i<nTemp ); - zP3 = zTemp; - break; - } - case P3_COLLSEQ: { - CollSeq *pColl = (CollSeq*)pOp->p3; - sprintf(zTemp, "collseq(%.20s)", pColl->zName); - zP3 = zTemp; - break; - } - case P3_FUNCDEF: { - FuncDef *pDef = (FuncDef*)pOp->p3; - char zNum[30]; - sprintf(zTemp, "%.*s", nTemp, pDef->zName); - sprintf(zNum,"(%d)", pDef->nArg); - if( strlen(zTemp)+strlen(zNum)+1<=nTemp ){ - strcat(zTemp, zNum); - } - zP3 = zTemp; - break; - } - default: { - zP3 = pOp->p3; - if( zP3==0 || pOp->opcode==OP_Noop ){ - zP3 = ""; - } - } - } - return zP3; -} - - -#if !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) -/* -** Print a single opcode. This routine is used for debugging only. -*/ -void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ - char *zP3; - char zPtr[50]; - static const char *zFormat1 = "%4d %-13s %4d %4d %s\n"; - if( pOut==0 ) pOut = stdout; - zP3 = displayP3(pOp, zPtr, sizeof(zPtr)); - fprintf(pOut, zFormat1, - pc, sqlite3OpcodeNames[pOp->opcode], pOp->p1, pOp->p2, zP3); - fflush(pOut); -} -#endif - -/* -** Release an array of N Mem elements -*/ -static void releaseMemArray(Mem *p, int N){ - if( p ){ - while( N-->0 ){ - sqlite3VdbeMemRelease(p++); - } - } -} - -/* -** Give a listing of the program in the virtual machine. -** -** The interface is the same as sqlite3VdbeExec(). But instead of -** running the code, it invokes the callback once for each instruction. -** This feature is used to implement "EXPLAIN". -*/ -int sqlite3VdbeList( - Vdbe *p /* The VDBE */ -){ - sqlite3 *db = p->db; - int i; - int rc = SQLITE_OK; - - assert( p->explain ); - - /* Even though this opcode does not put dynamic strings onto the - ** the stack, they may become dynamic if the user calls - ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. - */ - if( p->pTos==&p->aStack[4] ){ - releaseMemArray(p->aStack, 5); - } - p->resOnStack = 0; - - i = p->pc++; - if( i>=p->nOp ){ - p->rc = SQLITE_OK; - rc = SQLITE_DONE; - }else if( db->flags & SQLITE_Interrupt ){ - db->flags &= ~SQLITE_Interrupt; - if( db->magic!=SQLITE_MAGIC_BUSY ){ - p->rc = SQLITE_MISUSE; - }else{ - p->rc = SQLITE_INTERRUPT; - } - rc = SQLITE_ERROR; - sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(p->rc), (char*)0); - }else{ - Op *pOp = &p->aOp[i]; - Mem *pMem = p->aStack; - pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; - pMem->i = i; /* Program counter */ - pMem++; - - pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = sqlite3OpcodeNames[pOp->opcode]; /* Opcode */ - pMem->n = strlen(pMem->z); - pMem->type = SQLITE_TEXT; - pMem->enc = SQLITE_UTF8; - pMem++; - - pMem->flags = MEM_Int; - pMem->i = pOp->p1; /* P1 */ - pMem->type = SQLITE_INTEGER; - pMem++; - - pMem->flags = MEM_Int; - pMem->i = pOp->p2; /* P2 */ - pMem->type = SQLITE_INTEGER; - pMem++; - - pMem->flags = MEM_Short|MEM_Str|MEM_Term; /* P3 */ - pMem->z = displayP3(pOp, pMem->zShort, sizeof(pMem->zShort)); - pMem->type = SQLITE_TEXT; - pMem->enc = SQLITE_UTF8; - - p->nResColumn = 5; - p->pTos = pMem; - p->rc = SQLITE_OK; - p->resOnStack = 1; - rc = SQLITE_ROW; - } - return rc; -} - -/* -** Print the SQL that was used to generate a VDBE program. -*/ -void sqlite3VdbePrintSql(Vdbe *p){ -#ifdef SQLITE_DEBUG - int nOp = p->nOp; - VdbeOp *pOp; - if( nOp<1 ) return; - pOp = &p->aOp[nOp-1]; - if( pOp->opcode==OP_Noop && pOp->p3!=0 ){ - const char *z = pOp->p3; - while( isspace(*(u8*)z) ) z++; - printf("SQL: [%s]\n", z); - } -#endif -} - -/* -** Prepare a virtual machine for execution. This involves things such -** as allocating stack space and initializing the program counter. -** After the VDBE has be prepped, it can be executed by one or more -** calls to sqlite3VdbeExec(). -** -** This is the only way to move a VDBE from VDBE_MAGIC_INIT to -** VDBE_MAGIC_RUN. -*/ -void sqlite3VdbeMakeReady( - Vdbe *p, /* The VDBE */ - int nVar, /* Number of '?' see in the SQL statement */ - int nMem, /* Number of memory cells to allocate */ - int nCursor, /* Number of cursors to allocate */ - int isExplain /* True if the EXPLAIN keywords is present */ -){ - int n; - - assert( p!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - - /* There should be at least one opcode. - */ - assert( p->nOp>0 ); - - /* No instruction ever pushes more than a single element onto the - ** stack. And the stack never grows on successive executions of the - ** same loop. So the total number of instructions is an upper bound - ** on the maximum stack depth required. - ** - ** Allocation all the stack space we will ever need. - */ - if( p->aStack==0 ){ - resolveP2Values(p); - assert( nVar>=0 ); - n = isExplain ? 10 : p->nOp; - p->aStack = sqliteMalloc( - n*sizeof(p->aStack[0]) /* aStack */ - + n*sizeof(Mem*) /* apArg */ - + nVar*sizeof(Mem) /* aVar */ - + nVar*sizeof(char*) /* azVar */ - + nMem*sizeof(Mem) /* aMem */ - + nCursor*sizeof(Cursor*) /* apCsr */ - ); - if( !sqlite3_malloc_failed ){ - p->aMem = &p->aStack[n]; - p->nMem = nMem; - p->aVar = &p->aMem[nMem]; - p->nVar = nVar; - p->okVar = 0; - p->apArg = (Mem**)&p->aVar[nVar]; - p->azVar = (char**)&p->apArg[n]; - p->apCsr = (Cursor**)&p->azVar[nVar]; - p->nCursor = nCursor; - for(n=0; n<nVar; n++){ - p->aVar[n].flags = MEM_Null; - } - for(n=0; n<nMem; n++){ - p->aMem[n].flags = MEM_Null; - } - } - } - -#ifdef SQLITE_DEBUG - if( (p->db->flags & SQLITE_VdbeListing)!=0 - || sqlite3OsFileExists("vdbe_explain") - ){ - int i; - printf("VDBE Program Listing:\n"); - sqlite3VdbePrintSql(p); - for(i=0; i<p->nOp; i++){ - sqlite3VdbePrintOp(stdout, i, &p->aOp[i]); - } - } - if( sqlite3OsFileExists("vdbe_trace") ){ - p->trace = stdout; - } -#endif - p->pTos = &p->aStack[-1]; - p->pc = -1; - p->rc = SQLITE_OK; - p->uniqueCnt = 0; - p->returnDepth = 0; - p->errorAction = OE_Abort; - p->popStack = 0; - p->explain |= isExplain; - p->magic = VDBE_MAGIC_RUN; - p->nChange = 0; -#ifdef VDBE_PROFILE - { - int i; - for(i=0; i<p->nOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; - } - } -#endif -} - - -/* -** Remove any elements that remain on the sorter for the VDBE given. -*/ -void sqlite3VdbeSorterReset(Vdbe *p){ - while( p->pSort ){ - Sorter *pSorter = p->pSort; - p->pSort = pSorter->pNext; - sqliteFree(pSorter->zKey); - sqlite3VdbeMemRelease(&pSorter->data); - sqliteFree(pSorter); - } -} - -/* -** Free all resources allociated with AggElem pElem, an element of -** aggregate pAgg. -*/ -void freeAggElem(AggElem *pElem, Agg *pAgg){ - int i; - for(i=0; i<pAgg->nMem; i++){ - Mem *pMem = &pElem->aMem[i]; - if( pAgg->apFunc && pAgg->apFunc[i] && (pMem->flags & MEM_AggCtx)!=0 ){ - sqlite3_context ctx; - ctx.pFunc = pAgg->apFunc[i]; - ctx.s.flags = MEM_Null; - ctx.pAgg = pMem->z; - ctx.cnt = pMem->i; - ctx.isStep = 0; - ctx.isError = 0; - (*pAgg->apFunc[i]->xFinalize)(&ctx); - pMem->z = ctx.pAgg; - if( pMem->z!=0 && pMem->z!=pMem->zShort ){ - sqliteFree(pMem->z); - } - sqlite3VdbeMemRelease(&ctx.s); - }else{ - sqlite3VdbeMemRelease(pMem); - } - } - sqliteFree(pElem); -} - -/* -** Reset an Agg structure. Delete all its contents. -** -** For installable aggregate functions, if the step function has been -** called, make sure the finalizer function has also been called. The -** finalizer might need to free memory that was allocated as part of its -** private context. If the finalizer has not been called yet, call it -** now. -** -** If db is NULL, then this is being called from sqliteVdbeReset(). In -** this case clean up all references to the temp-table used for -** aggregates (if it was ever opened). -** -** If db is not NULL, then this is being called from with an OP_AggReset -** opcode. Open the temp-table, if it has not already been opened and -** delete the contents of the table used for aggregate information, ready -** for the next round of aggregate processing. -*/ -int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){ - int rc = 0; - BtCursor *pCsr = pAgg->pCsr; - - assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0) - || sqlite3_malloc_failed ); - - /* If pCsr is not NULL, then the table used for aggregate information - ** is open. Loop through it and free the AggElem* structure pointed at - ** by each entry. If the finalizer has not been called for an AggElem, - ** do that too. Finally, clear the btree table itself. - */ - if( pCsr ){ - int res; - assert( pAgg->pBtree ); - assert( pAgg->nTab>0 ); - - rc=sqlite3BtreeFirst(pCsr, &res); - while( res==0 && rc==SQLITE_OK ){ - AggElem *pElem; - rc = sqlite3BtreeData(pCsr, 0, sizeof(AggElem*), (char *)&pElem); - if( res!=SQLITE_OK ){ - return rc; - } - assert( pAgg->apFunc!=0 ); - freeAggElem(pElem, pAgg); - rc=sqlite3BtreeNext(pCsr, &res); - } - if( rc!=SQLITE_OK ){ - return rc; - } - - sqlite3BtreeCloseCursor(pCsr); - sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab); - }else{ - /* The cursor may not be open because the aggregator was never used, - ** or it could be that it was used but there was no GROUP BY clause. - */ - if( pAgg->pCurrent ){ - freeAggElem(pAgg->pCurrent, pAgg); - } - } - - /* If db is not NULL and we have not yet and we have not yet opened - ** the temporary btree then do so and create the table to store aggregate - ** information. - ** - ** If db is NULL, then close the temporary btree if it is open. - */ - if( db ){ - if( !pAgg->pBtree ){ - assert( pAgg->nTab==0 ); - rc = sqlite3BtreeFactory(db, ":memory:", 0, TEMP_PAGES, &pAgg->pBtree); - if( rc!=SQLITE_OK ) return rc; - sqlite3BtreeBeginTrans(pAgg->pBtree, 1); - rc = sqlite3BtreeCreateTable(pAgg->pBtree, &pAgg->nTab, 0); - if( rc!=SQLITE_OK ) return rc; - } - assert( pAgg->nTab!=0 ); - - rc = sqlite3BtreeCursor(pAgg->pBtree, pAgg->nTab, 1, - sqlite3VdbeRecordCompare, pKeyInfo, &pAgg->pCsr); - if( rc!=SQLITE_OK ) return rc; - }else{ - if( pAgg->pBtree ){ - sqlite3BtreeClose(pAgg->pBtree); - pAgg->pBtree = 0; - pAgg->nTab = 0; - } - pAgg->pCsr = 0; - } - - if( pAgg->apFunc ){ - sqliteFree(pAgg->apFunc); - pAgg->apFunc = 0; - } - pAgg->pCurrent = 0; - pAgg->nMem = 0; - pAgg->searching = 0; - return SQLITE_OK; -} - - -/* -** Delete a keylist -*/ -void sqlite3VdbeKeylistFree(Keylist *p){ - while( p ){ - Keylist *pNext = p->pNext; - sqliteFree(p); - p = pNext; - } -} - -/* -** Close a cursor and release all the resources that cursor happens -** to hold. -*/ -void sqlite3VdbeFreeCursor(Cursor *pCx){ - if( pCx==0 ){ - return; - } - if( pCx->pCursor ){ - sqlite3BtreeCloseCursor(pCx->pCursor); - } - if( pCx->pBt ){ - sqlite3BtreeClose(pCx->pBt); - } - sqliteFree(pCx->pData); - sqliteFree(pCx->aType); - sqliteFree(pCx); -} - -/* -** Close all cursors -*/ -static void closeAllCursors(Vdbe *p){ - int i; - if( p->apCsr==0 ) return; - for(i=0; i<p->nCursor; i++){ - sqlite3VdbeFreeCursor(p->apCsr[i]); - p->apCsr[i] = 0; - } -} - -/* -** Clean up the VM after execution. -** -** This routine will automatically close any cursors, lists, and/or -** sorters that were left open. It also deletes the values of -** variables in the aVar[] array. -*/ -static void Cleanup(Vdbe *p){ - int i; - if( p->aStack ){ - releaseMemArray(p->aStack, 1 + (p->pTos - p->aStack)); - p->pTos = &p->aStack[-1]; - } - closeAllCursors(p); - releaseMemArray(p->aMem, p->nMem); - if( p->pList ){ - sqlite3VdbeKeylistFree(p->pList); - p->pList = 0; - } - if( p->contextStack ){ - for(i=0; i<p->contextStackTop; i++){ - sqlite3VdbeKeylistFree(p->contextStack[i].pList); - } - sqliteFree(p->contextStack); - } - sqlite3VdbeSorterReset(p); - sqlite3VdbeAggReset(0, &p->agg, 0); - p->contextStack = 0; - p->contextStackDepth = 0; - p->contextStackTop = 0; - sqliteFree(p->zErrMsg); - p->zErrMsg = 0; -} - -/* -** Set the number of result columns that will be returned by this SQL -** statement. This is now set at compile time, rather than during -** execution of the vdbe program so that sqlite3_column_count() can -** be called on an SQL statement before sqlite3_step(). -*/ -void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ - Mem *pColName; - int n; - assert( 0==p->nResColumn ); - p->nResColumn = nResColumn; - n = nResColumn*2; - p->aColName = pColName = (Mem*)sqliteMalloc( sizeof(Mem)*n ); - if( p->aColName==0 ) return; - while( n-- > 0 ){ - (pColName++)->flags = MEM_Null; - } -} - -/* -** Set the name of the idx'th column to be returned by the SQL statement. -** zName must be a pointer to a nul terminated string. -** -** This call must be made after a call to sqlite3VdbeSetNumCols(). -** -** If N==P3_STATIC it means that zName is a pointer to a constant static -** string and we can just copy the pointer. If it is P3_DYNAMIC, then -** the string is freed using sqliteFree() when the vdbe is finished with -** it. Otherwise, N bytes of zName are copied. -*/ -int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){ - int rc; - Mem *pColName; - assert( idx<(2*p->nResColumn) ); - if( sqlite3_malloc_failed ) return SQLITE_NOMEM; - assert( p->aColName!=0 ); - pColName = &(p->aColName[idx]); - if( N==P3_DYNAMIC || N==P3_STATIC ){ - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, SQLITE_STATIC); - }else{ - rc = sqlite3VdbeMemSetStr(pColName, zName, N, SQLITE_UTF8,SQLITE_TRANSIENT); - } - if( rc==SQLITE_OK && N==P3_DYNAMIC ){ - pColName->flags = (pColName->flags&(~MEM_Static))|MEM_Dyn; - pColName->xDel = 0; - } - return rc; -} - -/* -** A read or write transaction may or may not be active on database handle -** db. If a transaction is active, commit it. If there is a -** write-transaction spanning more than one database file, this routine -** takes care of the master journal trickery. -*/ -static int vdbeCommit(sqlite3 *db){ - int i; - int nTrans = 0; /* Number of databases with an active write-transaction */ - int rc = SQLITE_OK; - int needXcommit = 0; - - for(i=0; i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt && sqlite3BtreeIsInTrans(pBt) ){ - needXcommit = 1; - if( i!=1 ) nTrans++; - } - } - - /* If there are any write-transactions at all, invoke the commit hook */ - if( needXcommit && db->xCommitCallback ){ - int rc; - sqlite3SafetyOff(db); - rc = db->xCommitCallback(db->pCommitArg); - sqlite3SafetyOn(db); - if( rc ){ - return SQLITE_CONSTRAINT; - } - } - - /* The simple case - no more than one database file (not counting the - ** TEMP database) has a transaction active. There is no need for the - ** master-journal. - ** - ** If the return value of sqlite3BtreeGetFilename() is a zero length - ** string, it means the main database is :memory:. In that case we do - ** not support atomic multi-file commits, so use the simple case then - ** too. - */ - if( 0==strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){ - for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite3BtreeSync(pBt, 0); - } - } - - /* Do the commit only if all databases successfully synced */ - if( rc==SQLITE_OK ){ - for(i=0; i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - sqlite3BtreeCommit(pBt); - } - } - } - } - - /* The complex case - There is a multi-file write-transaction active. - ** This requires a master journal file to ensure the transaction is - ** committed atomicly. - */ - else{ - char *zMaster = 0; /* File-name for the master journal */ - char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); - OsFile master; - - /* Select a master journal file name */ - do { - u32 random; - sqliteFree(zMaster); - sqlite3Randomness(sizeof(random), &random); - zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random&0x7fffffff); - if( !zMaster ){ - return SQLITE_NOMEM; - } - }while( sqlite3OsFileExists(zMaster) ); - - /* Open the master journal. */ - memset(&master, 0, sizeof(master)); - rc = sqlite3OsOpenExclusive(zMaster, &master, 0); - if( rc!=SQLITE_OK ){ - sqliteFree(zMaster); - return rc; - } - - /* Write the name of each database file in the transaction into the new - ** master journal file. If an error occurs at this point close - ** and delete the master journal file. All the individual journal files - ** still have 'null' as the master journal pointer, so they will roll - ** back independantly if a failure occurs. - */ - for(i=0; i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( i==1 ) continue; /* Ignore the TEMP database */ - if( pBt && sqlite3BtreeIsInTrans(pBt) ){ - char const *zFile = sqlite3BtreeGetJournalname(pBt); - if( zFile[0]==0 ) continue; /* Ignore :memory: databases */ - rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1); - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqlite3OsDelete(zMaster); - sqliteFree(zMaster); - return rc; - } - } - } - - - /* Sync the master journal file. Before doing this, open the directory - ** the master journal file is store in so that it gets synced too. - */ - zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); - rc = sqlite3OsOpenDirectory(zMainFile, &master); - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqlite3OsDelete(zMaster); - sqliteFree(zMaster); - return rc; - } - rc = sqlite3OsSync(&master); - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqliteFree(zMaster); - return rc; - } - - /* Sync all the db files involved in the transaction. The same call - ** sets the master journal pointer in each individual journal. If - ** an error occurs here, do not delete the master journal file. - ** - ** If the error occurs during the first call to sqlite3BtreeSync(), - ** then there is a chance that the master journal file will be - ** orphaned. But we cannot delete it, in case the master journal - ** file name was written into the journal file before the failure - ** occured. - */ - for(i=0; i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt && sqlite3BtreeIsInTrans(pBt) ){ - rc = sqlite3BtreeSync(pBt, zMaster); - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqliteFree(zMaster); - return rc; - } - } - } - sqlite3OsClose(&master); - - /* Delete the master journal file. This commits the transaction. After - ** doing this the directory is synced again before any individual - ** transaction files are deleted. - */ - rc = sqlite3OsDelete(zMaster); - assert( rc==SQLITE_OK ); - sqliteFree(zMaster); - zMaster = 0; - rc = sqlite3OsSyncDirectory(zMainFile); - if( rc!=SQLITE_OK ){ - /* This is not good. The master journal file has been deleted, but - ** the directory sync failed. There is no completely safe course of - ** action from here. The individual journals contain the name of the - ** master journal file, but there is no way of knowing if that - ** master journal exists now or if it will exist after the operating - ** system crash that may follow the fsync() failure. - */ - assert(0); - sqliteFree(zMaster); - return rc; - } - - /* All files and directories have already been synced, so the following - ** calls to sqlite3BtreeCommit() are only closing files and deleting - ** journals. If something goes wrong while this is happening we don't - ** really care. The integrity of the transaction is already guaranteed, - ** but some stray 'cold' journals may be lying around. Returning an - ** error code won't help matters. - */ - for(i=0; i<db->nDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - sqlite3BtreeCommit(pBt); - } - } - } - - return rc; -} - -/* -** Find every active VM other than pVdbe and change its status to -** aborted. This happens when one VM causes a rollback due to an -** ON CONFLICT ROLLBACK clause (for example). The other VMs must be -** aborted so that they do not have data rolled out from underneath -** them leading to a segfault. -*/ -static void abortOtherActiveVdbes(Vdbe *pVdbe){ - Vdbe *pOther; - for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){ - if( pOther==pVdbe ) continue; - if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; - closeAllCursors(pOther); - pOther->aborted = 1; - } -} - -/* -** This routine checks that the sqlite3.activeVdbeCnt count variable -** matches the number of vdbe's in the list sqlite3.pVdbe that are -** currently active. An assertion fails if the two counts do not match. -** This is an internal self-check only - it is not an essential processing -** step. -** -** This is a no-op if NDEBUG is defined. -*/ -#ifndef NDEBUG -static void checkActiveVdbeCnt(sqlite3 *db){ - Vdbe *p; - int cnt = 0; - p = db->pVdbe; - while( p ){ - if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){ - cnt++; - } - p = p->pNext; - } - assert( cnt==db->activeVdbeCnt ); -} -#else -#define checkActiveVdbeCnt(x) -#endif - -/* -** This routine is called the when a VDBE tries to halt. If the VDBE -** has made changes and is in autocommit mode, then commit those -** changes. If a rollback is needed, then do the rollback. -** -** This routine is the only way to move the state of a VM from -** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. -** -** Return an error code. If the commit could not complete because of -** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it -** means the close did not happen and needs to be repeated. -*/ -int sqlite3VdbeHalt(Vdbe *p){ - sqlite3 *db = p->db; - int i; - int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */ - - if( p->magic!=VDBE_MAGIC_RUN ){ - /* Already halted. Nothing to do. */ - assert( p->magic==VDBE_MAGIC_HALT ); - return SQLITE_OK; - } - closeAllCursors(p); - checkActiveVdbeCnt(db); - if( db->autoCommit && db->activeVdbeCnt==1 ){ - if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - /* The auto-commit flag is true, there are no other active queries - ** using this handle and the vdbe program was successful or hit an - ** 'OR FAIL' constraint. This means a commit is required. - */ - int rc = vdbeCommit(db); - if( rc==SQLITE_BUSY ){ - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - xFunc = sqlite3BtreeRollback; - } - }else{ - xFunc = sqlite3BtreeRollback; - } - }else{ - if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - xFunc = sqlite3BtreeCommitStmt; - }else if( p->errorAction==OE_Abort ){ - xFunc = sqlite3BtreeRollbackStmt; - }else{ - xFunc = sqlite3BtreeRollback; - db->autoCommit = 1; - abortOtherActiveVdbes(p); - } - } - - /* If xFunc is not NULL, then it is one of sqlite3BtreeRollback, - ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on - ** each backend. If an error occurs and the return code is still - ** SQLITE_OK, set the return code to the new error value. - */ - for(i=0; xFunc && i<db->nDb; i++){ - int rc; - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = xFunc(pBt); - if( p->rc==SQLITE_OK ) p->rc = rc; - } - } - - /* If this was an INSERT, UPDATE or DELETE, set the change counter. */ - if( p->changeCntOn ){ - if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){ - sqlite3VdbeSetChanges(db, p->nChange); - }else{ - sqlite3VdbeSetChanges(db, 0); - } - p->nChange = 0; - } - - /* Rollback or commit any schema changes that occurred. */ - if( p->rc!=SQLITE_OK ){ - sqlite3RollbackInternalChanges(db); - }else if( db->flags & SQLITE_InternChanges ){ - sqlite3CommitInternalChanges(db); - } - - /* We have successfully halted and closed the VM. Record this fact. */ - if( p->pc>=0 ){ - db->activeVdbeCnt--; - } - p->magic = VDBE_MAGIC_HALT; - checkActiveVdbeCnt(db); - - return SQLITE_OK; -} - -/* -** Clean up a VDBE after execution but do not delete the VDBE just yet. -** Write any error messages into *pzErrMsg. Return the result code. -** -** After this routine is run, the VDBE should be ready to be executed -** again. -** -** To look at it another way, this routine resets the state of the -** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to -** VDBE_MAGIC_INIT. -*/ -int sqlite3VdbeReset(Vdbe *p){ - if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){ - sqlite3Error(p->db, SQLITE_MISUSE, 0); - return SQLITE_MISUSE; - } - - /* If the VM did not run to completion or if it encountered an - ** error, then it might not have been halted properly. So halt - ** it now. - */ - sqlite3VdbeHalt(p); - - /* Transfer the error code and error message from the VDBE into the - ** main database structure. - */ - if( p->zErrMsg ){ - sqlite3Error(p->db, p->rc, "%s", p->zErrMsg); - sqliteFree(p->zErrMsg); - p->zErrMsg = 0; - }else if( p->rc ){ - sqlite3Error(p->db, p->rc, 0); - }else{ - sqlite3Error(p->db, SQLITE_OK, 0); - } - - /* Reclaim all memory used by the VDBE - */ - Cleanup(p); - - /* Save profiling information from this VDBE run. - */ - assert( p->pTos<&p->aStack[p->pc<0?0:p->pc] || sqlite3_malloc_failed==1 ); -#ifdef VDBE_PROFILE - { - FILE *out = fopen("vdbe_profile.out", "a"); - if( out ){ - int i; - fprintf(out, "---- "); - for(i=0; i<p->nOp; i++){ - fprintf(out, "%02x", p->aOp[i].opcode); - } - fprintf(out, "\n"); - for(i=0; i<p->nOp; i++){ - fprintf(out, "%6d %10lld %8lld ", - p->aOp[i].cnt, - p->aOp[i].cycles, - p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 - ); - sqlite3VdbePrintOp(out, i, &p->aOp[i]); - } - fclose(out); - } - } -#endif - p->magic = VDBE_MAGIC_INIT; - p->aborted = 0; - return p->rc; -} - -/* -** Clean up and delete a VDBE after execution. Return an integer which is -** the result code. Write any error message text into *pzErrMsg. -*/ -int sqlite3VdbeFinalize(Vdbe *p){ - int rc = SQLITE_OK; - sqlite3 *db = p->db; - - if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ - rc = sqlite3VdbeReset(p); - }else if( p->magic!=VDBE_MAGIC_INIT ){ - /* sqlite3Error(p->db, SQLITE_MISUSE, 0); */ - return SQLITE_MISUSE; - } - sqlite3VdbeDelete(p); - if( rc==SQLITE_SCHEMA ){ - sqlite3ResetInternalSchema(db, 0); - } - return rc; -} - -/* -** Call the destructor for each auxdata entry in pVdbeFunc for which -** the corresponding bit in mask is clear. Auxdata entries beyond 31 -** are always destroyed. To destroy all auxdata entries, call this -** routine with mask==0. -*/ -void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ - int i; - for(i=0; i<pVdbeFunc->nAux; i++){ - struct AuxData *pAux = &pVdbeFunc->apAux[i]; - if( (i>31 || !(mask&(1<<i))) && pAux->pAux ){ - if( pAux->xDelete ){ - pAux->xDelete(pAux->pAux); - } - pAux->pAux = 0; - } - } -} - -/* -** Delete an entire VDBE. -*/ -void sqlite3VdbeDelete(Vdbe *p){ - int i; - if( p==0 ) return; - Cleanup(p); - if( p->pPrev ){ - p->pPrev->pNext = p->pNext; - }else{ - assert( p->db->pVdbe==p ); - p->db->pVdbe = p->pNext; - } - if( p->pNext ){ - p->pNext->pPrev = p->pPrev; - } - if( p->aOp ){ - for(i=0; i<p->nOp; i++){ - Op *pOp = &p->aOp[i]; - if( pOp->p3type==P3_DYNAMIC || pOp->p3type==P3_KEYINFO ){ - sqliteFree(pOp->p3); - } - if( pOp->p3type==P3_VDBEFUNC ){ - VdbeFunc *pVdbeFunc = (VdbeFunc *)pOp->p3; - sqlite3VdbeDeleteAuxData(pVdbeFunc, 0); - sqliteFree(pVdbeFunc); - } - } - sqliteFree(p->aOp); - } - releaseMemArray(p->aVar, p->nVar); - sqliteFree(p->aLabel); - sqliteFree(p->aStack); - releaseMemArray(p->aColName, p->nResColumn*2); - sqliteFree(p->aColName); - p->magic = VDBE_MAGIC_DEAD; - sqliteFree(p); -} - -/* -** If a MoveTo operation is pending on the given cursor, then do that -** MoveTo now. Return an error code. If no MoveTo is pending, this -** routine does nothing and returns SQLITE_OK. -*/ -int sqlite3VdbeCursorMoveto(Cursor *p){ - if( p->deferredMoveto ){ - int res; - extern int sqlite3_search_count; - assert( p->intKey ); - if( p->intKey ){ - sqlite3BtreeMoveto(p->pCursor, 0, p->movetoTarget, &res); - }else{ - sqlite3BtreeMoveto(p->pCursor,(char*)&p->movetoTarget,sizeof(i64),&res); - } - *p->pIncrKey = 0; - p->lastRecno = keyToInt(p->movetoTarget); - p->recnoIsValid = res==0; - if( res<0 ){ - sqlite3BtreeNext(p->pCursor, &res); - } - sqlite3_search_count++; - p->deferredMoveto = 0; - p->cacheValid = 0; - } - return SQLITE_OK; -} - -/* -** The following functions: -** -** sqlite3VdbeSerialType() -** sqlite3VdbeSerialTypeLen() -** sqlite3VdbeSerialRead() -** sqlite3VdbeSerialLen() -** sqlite3VdbeSerialWrite() -** -** encapsulate the code that serializes values for storage in SQLite -** data and index records. Each serialized value consists of a -** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned -** integer, stored as a varint. -** -** In an SQLite index record, the serial type is stored directly before -** the blob of data that it corresponds to. In a table record, all serial -** types are stored at the start of the record, and the blobs of data at -** the end. Hence these functions allow the caller to handle the -** serial-type and data blob seperately. -** -** The following table describes the various storage classes for data: -** -** serial type bytes of data type -** -------------- --------------- --------------- -** 0 0 NULL -** 1 1 signed integer -** 2 2 signed integer -** 3 3 signed integer -** 4 4 signed integer -** 5 6 signed integer -** 6 8 signed integer -** 7 8 IEEE float -** 8-11 reserved for expansion -** N>=12 and even (N-12)/2 BLOB -** N>=13 and odd (N-13)/2 text -** -*/ - -/* -** Return the serial-type for the value stored in pMem. -*/ -u32 sqlite3VdbeSerialType(Mem *pMem){ - int flags = pMem->flags; - - if( flags&MEM_Null ){ - return 0; - } - if( flags&MEM_Int ){ - /* Figure out whether to use 1, 2, 4 or 8 bytes. */ - i64 i = pMem->i; - if( i>=-127 && i<=127 ) return 1; - if( i>=-32767 && i<=32767 ) return 2; - if( i>=-8388607 && i<=8388607 ) return 3; - if( i>=-2147483647 && i<=2147483647 ) return 4; - if( i>=-140737488355328L && i<=140737488355328L ) return 5; - return 6; - } - if( flags&MEM_Real ){ - return 7; - } - if( flags&MEM_Str ){ - int n = pMem->n; - assert( n>=0 ); - return ((n*2) + 13); - } - if( flags&MEM_Blob ){ - return (pMem->n*2 + 12); - } - return 0; -} - -/* -** Return the length of the data corresponding to the supplied serial-type. -*/ -int sqlite3VdbeSerialTypeLen(u32 serial_type){ - if( serial_type>=12 ){ - return (serial_type-12)/2; - }else{ - static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 }; - return aSize[serial_type]; - } -} - -/* -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -*/ -int sqlite3VdbeSerialPut(unsigned char *buf, Mem *pMem){ - u32 serial_type = sqlite3VdbeSerialType(pMem); - int len; - - /* NULL */ - if( serial_type==0 ){ - return 0; - } - - /* Integer and Real */ - if( serial_type<=7 ){ - u64 v; - int i; - if( serial_type==7 ){ - v = *(u64*)&pMem->r; - }else{ - v = *(u64*)&pMem->i; - } - len = i = sqlite3VdbeSerialTypeLen(serial_type); - while( i-- ){ - buf[i] = (v&0xFF); - v >>= 8; - } - return len; - } - - /* String or blob */ - assert( serial_type>=12 ); - len = sqlite3VdbeSerialTypeLen(serial_type); - memcpy(buf, pMem->z, len); - return len; -} - -/* -** Deserialize the data blob pointed to by buf as serial type serial_type -** and store the result in pMem. Return the number of bytes read. -*/ -int sqlite3VdbeSerialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -){ - int len; - - if( serial_type==0 ){ - /* NULL */ - pMem->flags = MEM_Null; - return 0; - } - len = sqlite3VdbeSerialTypeLen(serial_type); - if( serial_type<=7 ){ - /* Integer and Real */ - if( serial_type<=4 ){ - /* 32-bit integer type. This is handled by a special case for - ** performance reasons. */ - int v = buf[0]; - int n; - if( v&0x80 ){ - v |= -256; - } - for(n=1; n<len; n++){ - v = (v<<8) | buf[n]; - } - pMem->flags = MEM_Int; - pMem->i = v; - return n; - }else{ - u64 v = 0; - int n; - - if( buf[0]&0x80 ){ - v = -1; - } - for(n=0; n<len; n++){ - v = (v<<8) | buf[n]; - } - if( serial_type==7 ){ - pMem->flags = MEM_Real; - pMem->r = *(double*)&v; - }else{ - pMem->flags = MEM_Int; - pMem->i = *(i64*)&v; - } - } - }else{ - /* String or blob */ - assert( serial_type>=12 ); - pMem->z = (char *)buf; - pMem->n = len; - pMem->xDel = 0; - if( serial_type&0x01 ){ - pMem->flags = MEM_Str | MEM_Ephem; - }else{ - pMem->flags = MEM_Blob | MEM_Ephem; - } - } - return len; -} - -/* -** This function compares the two table rows or index records specified by -** {nKey1, pKey1} and {nKey2, pKey2}, returning a negative, zero -** or positive integer if {nKey1, pKey1} is less than, equal to or -** greater than {nKey2, pKey2}. Both Key1 and Key2 must be byte strings -** composed by the OP_MakeRecord opcode of the VDBE. -*/ -int sqlite3VdbeRecordCompare( - void *userData, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - KeyInfo *pKeyInfo = (KeyInfo*)userData; - u32 d1, d2; /* Offset into aKey[] of next data element */ - u32 idx1, idx2; /* Offset into aKey[] of next header element */ - u32 szHdr1, szHdr2; /* Number of bytes in header */ - int i = 0; - int nField; - int rc = 0; - const unsigned char *aKey1 = (const unsigned char *)pKey1; - const unsigned char *aKey2 = (const unsigned char *)pKey2; - - Mem mem1; - Mem mem2; - mem1.enc = pKeyInfo->enc; - mem2.enc = pKeyInfo->enc; - - idx1 = sqlite3GetVarint32(pKey1, &szHdr1); - d1 = szHdr1; - idx2 = sqlite3GetVarint32(pKey2, &szHdr2); - d2 = szHdr2; - nField = pKeyInfo->nField; - while( idx1<szHdr1 && idx2<szHdr2 ){ - u32 serial_type1; - u32 serial_type2; - - /* Read the serial types for the next element in each key. */ - idx1 += sqlite3GetVarint32(&aKey1[idx1], &serial_type1); - if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break; - idx2 += sqlite3GetVarint32(&aKey2[idx2], &serial_type2); - if( d2>=nKey2 && sqlite3VdbeSerialTypeLen(serial_type2)>0 ) break; - - /* Assert that there is enough space left in each key for the blob of - ** data to go with the serial type just read. This assert may fail if - ** the file is corrupted. Then read the value from each key into mem1 - ** and mem2 respectively. - */ - d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); - d2 += sqlite3VdbeSerialGet(&aKey2[d2], serial_type2, &mem2); - - rc = sqlite3MemCompare(&mem1, &mem2, i<nField ? pKeyInfo->aColl[i] : 0); - sqlite3VdbeMemRelease(&mem1); - sqlite3VdbeMemRelease(&mem2); - if( rc!=0 ){ - break; - } - i++; - } - - /* One of the keys ran out of fields, but all the fields up to that point - ** were equal. If the incrKey flag is true, then the second key is - ** treated as larger. - */ - if( rc==0 ){ - if( pKeyInfo->incrKey ){ - rc = -1; - }else if( d1<nKey1 ){ - rc = 1; - }else if( d2<nKey2 ){ - rc = -1; - } - } - - if( pKeyInfo->aSortOrder && i<pKeyInfo->nField && pKeyInfo->aSortOrder[i] ){ - rc = -rc; - } - - return rc; -} - -/* -** The argument is an index entry composed using the OP_MakeRecord opcode. -** The last entry in this record should be an integer (specifically -** an integer rowid). This routine returns the number of bytes in -** that integer. -*/ -int sqlite3VdbeIdxRowidLen(int nKey, const u8 *aKey){ - u32 szHdr; /* Size of the header */ - u32 typeRowid; /* Serial type of the rowid */ - - sqlite3GetVarint32(aKey, &szHdr); - sqlite3GetVarint32(&aKey[szHdr-1], &typeRowid); - return sqlite3VdbeSerialTypeLen(typeRowid); -} - - -/* -** pCur points at an index entry created using the OP_MakeRecord opcode. -** Read the rowid (the last field in the record) and store it in *rowid. -** Return SQLITE_OK if everything works, or an error code otherwise. -*/ -int sqlite3VdbeIdxRowid(BtCursor *pCur, i64 *rowid){ - i64 nCellKey; - int rc; - u32 szHdr; /* Size of the header */ - u32 typeRowid; /* Serial type of the rowid */ - u32 lenRowid; /* Size of the rowid */ - Mem m, v; - - sqlite3BtreeKeySize(pCur, &nCellKey); - if( nCellKey<=0 ){ - return SQLITE_CORRUPT; - } - rc = sqlite3VdbeMemFromBtree(pCur, 0, nCellKey, 1, &m); - if( rc ){ - return rc; - } - sqlite3GetVarint32(m.z, &szHdr); - sqlite3GetVarint32(&m.z[szHdr-1], &typeRowid); - lenRowid = sqlite3VdbeSerialTypeLen(typeRowid); - sqlite3VdbeSerialGet(&m.z[m.n-lenRowid], typeRowid, &v); - *rowid = v.i; - sqlite3VdbeMemRelease(&m); - return SQLITE_OK; -} - -/* -** Compare the key of the index entry that cursor pC is point to against -** the key string in pKey (of length nKey). Write into *pRes a number -** that is negative, zero, or positive if pC is less than, equal to, -** or greater than pKey. Return SQLITE_OK on success. -** -** pKey is either created without a rowid or is truncated so that it -** omits the rowid at the end. The rowid at the end of the index entry -** is ignored as well. -*/ -int sqlite3VdbeIdxKeyCompare( - Cursor *pC, /* The cursor to compare against */ - int nKey, const u8 *pKey, /* The key to compare */ - int *res /* Write the comparison result here */ -){ - i64 nCellKey; - int rc; - BtCursor *pCur = pC->pCursor; - int lenRowid; - Mem m; - - sqlite3BtreeKeySize(pCur, &nCellKey); - if( nCellKey<=0 ){ - *res = 0; - return SQLITE_OK; - } - rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m); - if( rc ){ - return rc; - } - lenRowid = sqlite3VdbeIdxRowidLen(m.n, m.z); - *res = sqlite3VdbeRecordCompare(pC->pKeyInfo, m.n-lenRowid, m.z, nKey, pKey); - sqlite3VdbeMemRelease(&m); - return SQLITE_OK; -} - -/* -** This routine sets the value to be returned by subsequent calls to -** sqlite3_changes() on the database handle 'db'. -*/ -void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){ - db->nChange = nChange; - db->nTotalChange += nChange; -} - -/* -** Set a flag in the vdbe to update the change counter when it is finalised -** or reset. -*/ -void sqlite3VdbeCountChanges(Vdbe *p){ - p->changeCntOn = 1; -} diff --git a/kopete/plugins/statistics/sqlite/vdbemem.c b/kopete/plugins/statistics/sqlite/vdbemem.c deleted file mode 100644 index c6cd94e6..00000000 --- a/kopete/plugins/statistics/sqlite/vdbemem.c +++ /dev/null @@ -1,724 +0,0 @@ -/* -** 2004 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to manipulate "Mem" structure. A "Mem" -** stores a single value in the VDBE. Mem is an opaque structure visible -** only within the VDBE. Interface routines refer to a Mem using the -** name sqlite_value -*/ -#include "sqliteInt.h" -#include "os.h" -#include <ctype.h> -#include "vdbeInt.h" - -/* -** If pMem is an object with a valid string representation, this routine -** ensures the internal encoding for the string representation is -** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE. -** -** If pMem is not a string object, or the encoding of the string -** representation is already stored using the requested encoding, then this -** routine is a no-op. -** -** SQLITE_OK is returned if the conversion is successful (or not required). -** SQLITE_NOMEM may be returned if a malloc() fails during conversion -** between formats. -*/ -int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ - if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ - return SQLITE_OK; - } - return sqlite3VdbeMemTranslate(pMem, desiredEnc); -} - -/* -** Make the given Mem object MEM_Dyn. -** -** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. -*/ -int sqlite3VdbeMemDynamicify(Mem *pMem){ - int n = pMem->n; - u8 *z; - if( (pMem->flags & (MEM_Ephem|MEM_Static|MEM_Short))==0 ){ - return SQLITE_OK; - } - assert( (pMem->flags & MEM_Dyn)==0 ); - assert( pMem->flags & (MEM_Str|MEM_Blob) ); - z = sqliteMallocRaw( n+2 ); - if( z==0 ){ - return SQLITE_NOMEM; - } - pMem->flags |= MEM_Dyn|MEM_Term; - pMem->xDel = 0; - memcpy(z, pMem->z, n ); - z[n] = 0; - z[n+1] = 0; - pMem->z = z; - pMem->flags &= ~(MEM_Ephem|MEM_Static|MEM_Short); - return SQLITE_OK; -} - -/* -** Make the given Mem object either MEM_Short or MEM_Dyn so that bytes -** of the Mem.z[] array can be modified. -** -** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. -*/ -int sqlite3VdbeMemMakeWriteable(Mem *pMem){ - int n; - u8 *z; - if( (pMem->flags & (MEM_Ephem|MEM_Static))==0 ){ - return SQLITE_OK; - } - assert( (pMem->flags & MEM_Dyn)==0 ); - assert( pMem->flags & (MEM_Str|MEM_Blob) ); - if( (n = pMem->n)+2<sizeof(pMem->zShort) ){ - z = pMem->zShort; - pMem->flags |= MEM_Short|MEM_Term; - }else{ - z = sqliteMallocRaw( n+2 ); - if( z==0 ){ - return SQLITE_NOMEM; - } - pMem->flags |= MEM_Dyn|MEM_Term; - pMem->xDel = 0; - } - memcpy(z, pMem->z, n ); - z[n] = 0; - z[n+1] = 0; - pMem->z = z; - pMem->flags &= ~(MEM_Ephem|MEM_Static); - return SQLITE_OK; -} - -/* -** Make sure the given Mem is \u0000 terminated. -*/ -int sqlite3VdbeMemNulTerminate(Mem *pMem){ - /* In SQLite, a string without a nul terminator occurs when a string - ** is loaded from disk (in this case the memory management is ephemeral), - ** or when it is supplied by the user as a bound variable or function - ** return value. Therefore, the memory management of the string must be - ** either ephemeral, static or controlled by a user-supplied destructor. - */ - assert( - !(pMem->flags&MEM_Str) || /* it's not a string, or */ - (pMem->flags&MEM_Term) || /* it's nul term. already, or */ - (pMem->flags&(MEM_Ephem|MEM_Static)) || /* it's static or ephem, or */ - (pMem->flags&MEM_Dyn && pMem->xDel) /* external management */ - ); - if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){ - return SQLITE_OK; /* Nothing to do */ - } - - if( pMem->flags & (MEM_Static|MEM_Ephem) ){ - return sqlite3VdbeMemMakeWriteable(pMem); - }else{ - char *z = sqliteMalloc(pMem->n+2); - if( !z ) return SQLITE_NOMEM; - memcpy(z, pMem->z, pMem->n); - z[pMem->n] = 0; - z[pMem->n+1] = 0; - pMem->xDel(pMem->z); - pMem->xDel = 0; - pMem->z = z; - } - return SQLITE_OK; -} - -/* -** Add MEM_Str to the set of representations for the given Mem. Numbers -** are converted using sqlite3_snprintf(). Converting a BLOB to a string -** is a no-op. -** -** Existing representations MEM_Int and MEM_Real are *not* invalidated. -** -** A MEM_Null value will never be passed to this function. This function is -** used for converting values to text for returning to the user (i.e. via -** sqlite3_value_text()), or for ensuring that values to be used as btree -** keys are strings. In the former case a NULL pointer is returned the -** user and the later is an internal programming error. -*/ -int sqlite3VdbeMemStringify(Mem *pMem, int enc){ - int rc = SQLITE_OK; - int fg = pMem->flags; - u8 *z = pMem->zShort; - - assert( !(fg&(MEM_Str|MEM_Blob)) ); - assert( fg&(MEM_Int|MEM_Real) ); - - /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8 - ** string representation of the value. Then, if the required encoding - ** is UTF-16le or UTF-16be do a translation. - ** - ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16. - */ - if( fg & MEM_Real ){ - sqlite3_snprintf(NBFS, z, "%.15g", pMem->r); - }else{ - assert( fg & MEM_Int ); - sqlite3_snprintf(NBFS, z, "%lld", pMem->i); - } - pMem->n = strlen(z); - pMem->z = z; - pMem->enc = SQLITE_UTF8; - pMem->flags |= MEM_Str | MEM_Short | MEM_Term; - sqlite3VdbeChangeEncoding(pMem, enc); - return rc; -} - -/* -** Release any memory held by the Mem. This may leave the Mem in an -** inconsistent state, for example with (Mem.z==0) and -** (Mem.type==SQLITE_TEXT). -*/ -void sqlite3VdbeMemRelease(Mem *p){ - if( p->flags & MEM_Dyn ){ - if( p->xDel ){ - p->xDel((void *)p->z); - }else{ - sqliteFree(p->z); - } - p->z = 0; - p->xDel = 0; - } -} - -/* -** Return some kind of integer value which is the best we can do -** at representing the value that *pMem describes as an integer. -** If pMem is an integer, then the value is exact. If pMem is -** a floating-point then the value returned is the integer part. -** If pMem is a string or blob, then we make an attempt to convert -** it into a integer and return that. If pMem is NULL, return 0. -** -** If pMem is a string, its encoding might be changed. -*/ -i64 sqlite3VdbeIntValue(Mem *pMem){ - int flags = pMem->flags; - if( flags & MEM_Int ){ - return pMem->i; - }else if( flags & MEM_Real ){ - return (i64)pMem->r; - }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value; - if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) - || sqlite3VdbeMemNulTerminate(pMem) ){ - return SQLITE_NOMEM; - } - assert( pMem->z ); - sqlite3atoi64(pMem->z, &value); - return value; - }else{ - return 0; - } -} - -/* -** Convert pMem to type integer. Invalidate any prior representations. -*/ -int sqlite3VdbeMemIntegerify(Mem *pMem){ - pMem->i = sqlite3VdbeIntValue(pMem); - sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Int; - return SQLITE_OK; -} - -/* -** Return the best representation of pMem that we can get into a -** double. If pMem is already a double or an integer, return its -** value. If it is a string or blob, try to convert it to a double. -** If it is a NULL, return 0.0. -*/ -double sqlite3VdbeRealValue(Mem *pMem){ - if( pMem->flags & MEM_Real ){ - return pMem->r; - }else if( pMem->flags & MEM_Int ){ - return (double)pMem->i; - }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ - if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) - || sqlite3VdbeMemNulTerminate(pMem) ){ - return SQLITE_NOMEM; - } - assert( pMem->z ); - return sqlite3AtoF(pMem->z, 0); - }else{ - return 0.0; - } -} - -/* -** Convert pMem so that it is of type MEM_Real. Invalidate any -** prior representations. -*/ -int sqlite3VdbeMemRealify(Mem *pMem){ - pMem->r = sqlite3VdbeRealValue(pMem); - sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Real; - return SQLITE_OK; -} - -/* -** Delete any previous value and set the value stored in *pMem to NULL. -*/ -void sqlite3VdbeMemSetNull(Mem *pMem){ - sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Null; - pMem->type = SQLITE_NULL; -} - -/* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type INTEGER. -*/ -void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ - sqlite3VdbeMemRelease(pMem); - pMem->i = val; - pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; -} - -/* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type REAL. -*/ -void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ - sqlite3VdbeMemRelease(pMem); - pMem->r = val; - pMem->flags = MEM_Real; - pMem->type = SQLITE_FLOAT; -} - -/* -** Make an shallow copy of pFrom into pTo. Prior contents of -** pTo are overwritten. The pFrom->z field is not duplicated. If -** pFrom->z is used, then pTo->z points to the same thing as pFrom->z -** and flags gets srcType (either MEM_Ephem or MEM_Static). -*/ -void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ - memcpy(pTo, pFrom, sizeof(*pFrom)-sizeof(pFrom->zShort)); - pTo->xDel = 0; - if( pTo->flags & (MEM_Str|MEM_Blob) ){ - pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Short|MEM_Ephem); - assert( srcType==MEM_Ephem || srcType==MEM_Static ); - pTo->flags |= srcType; - } -} - -/* -** Make a full copy of pFrom into pTo. Prior contents of pTo are -** freed before the copy is made. -*/ -int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ - int rc; - if( pTo->flags & MEM_Dyn ){ - sqlite3VdbeMemRelease(pTo); - } - sqlite3VdbeMemShallowCopy(pTo, pFrom, MEM_Ephem); - if( pTo->flags & MEM_Ephem ){ - rc = sqlite3VdbeMemMakeWriteable(pTo); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Transfer the contents of pFrom to pTo. Any existing value in pTo is -** freed. If pFrom contains ephemeral data, a copy is made. -** -** pFrom contains an SQL NULL when this routine returns. SQLITE_NOMEM -** might be returned if pFrom held ephemeral data and we were unable -** to allocate enough space to make a copy. -*/ -int sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ - int rc; - if( pTo->flags & MEM_Dyn ){ - sqlite3VdbeMemRelease(pTo); - } - memcpy(pTo, pFrom, sizeof(Mem)); - if( pFrom->flags & MEM_Short ){ - pTo->z = pTo->zShort; - } - pFrom->flags = MEM_Null; - pFrom->xDel = 0; - if( pTo->flags & MEM_Ephem ){ - rc = sqlite3VdbeMemMakeWriteable(pTo); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Change the value of a Mem to be a string or a BLOB. -*/ -int sqlite3VdbeMemSetStr( - Mem *pMem, /* Memory cell to set to string value */ - const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ -){ - sqlite3VdbeMemRelease(pMem); - if( !z ){ - pMem->flags = MEM_Null; - pMem->type = SQLITE_NULL; - return SQLITE_OK; - } - - pMem->z = (char *)z; - if( xDel==SQLITE_STATIC ){ - pMem->flags = MEM_Static; - }else if( xDel==SQLITE_TRANSIENT ){ - pMem->flags = MEM_Ephem; - }else{ - pMem->flags = MEM_Dyn; - pMem->xDel = xDel; - } - - pMem->enc = enc; - pMem->type = enc==0 ? SQLITE_BLOB : SQLITE_TEXT; - pMem->n = n; - - switch( enc ){ - case 0: - pMem->flags |= MEM_Blob; - break; - - case SQLITE_UTF8: - pMem->flags |= MEM_Str; - if( n<0 ){ - pMem->n = strlen(z); - pMem->flags |= MEM_Term; - } - break; - - case SQLITE_UTF16LE: - case SQLITE_UTF16BE: - pMem->flags |= MEM_Str; - if( pMem->n<0 ){ - pMem->n = sqlite3utf16ByteLen(pMem->z,-1); - pMem->flags |= MEM_Term; - } - if( sqlite3VdbeMemHandleBom(pMem) ){ - return SQLITE_NOMEM; - } - break; - - default: - assert(0); - } - if( pMem->flags&MEM_Ephem ){ - return sqlite3VdbeMemMakeWriteable(pMem); - } - return SQLITE_OK; -} - -/* -** Compare the values contained by the two memory cells, returning -** negative, zero or positive if pMem1 is less than, equal to, or greater -** than pMem2. Sorting order is NULL's first, followed by numbers (integers -** and reals) sorted numerically, followed by text ordered by the collating -** sequence pColl and finally blob's ordered by memcmp(). -** -** Two NULL values are considered equal by this function. -*/ -int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ - int rc; - int f1, f2; - int combined_flags; - - /* Interchange pMem1 and pMem2 if the collating sequence specifies - ** DESC order. - */ - f1 = pMem1->flags; - f2 = pMem2->flags; - combined_flags = f1|f2; - - /* If one value is NULL, it is less than the other. If both values - ** are NULL, return 0. - */ - if( combined_flags&MEM_Null ){ - return (f2&MEM_Null) - (f1&MEM_Null); - } - - /* If one value is a number and the other is not, the number is less. - ** If both are numbers, compare as reals if one is a real, or as integers - ** if both values are integers. - */ - if( combined_flags&(MEM_Int|MEM_Real) ){ - if( !(f1&(MEM_Int|MEM_Real)) ){ - return 1; - } - if( !(f2&(MEM_Int|MEM_Real)) ){ - return -1; - } - if( (f1 & f2 & MEM_Int)==0 ){ - double r1, r2; - if( (f1&MEM_Real)==0 ){ - r1 = pMem1->i; - }else{ - r1 = pMem1->r; - } - if( (f2&MEM_Real)==0 ){ - r2 = pMem2->i; - }else{ - r2 = pMem2->r; - } - if( r1<r2 ) return -1; - if( r1>r2 ) return 1; - return 0; - }else{ - assert( f1&MEM_Int ); - assert( f2&MEM_Int ); - if( pMem1->i < pMem2->i ) return -1; - if( pMem1->i > pMem2->i ) return 1; - return 0; - } - } - - /* If one value is a string and the other is a blob, the string is less. - ** If both are strings, compare using the collating functions. - */ - if( combined_flags&MEM_Str ){ - if( (f1 & MEM_Str)==0 ){ - return 1; - } - if( (f2 & MEM_Str)==0 ){ - return -1; - } - - assert( pMem1->enc==pMem2->enc ); - assert( pMem1->enc==SQLITE_UTF8 || - pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); - - /* This assert may fail if the collation sequence is deleted after this - ** vdbe program is compiled. The documentation defines this as an - ** undefined condition. A crash is usual result. - */ - assert( !pColl || pColl->xCmp ); - - if( pColl ){ - if( pMem1->enc==pColl->enc ){ - return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); - }else{ - u8 origEnc = pMem1->enc; - rc = pColl->xCmp( - pColl->pUser, - sqlite3ValueBytes((sqlite3_value*)pMem1, pColl->enc), - sqlite3ValueText((sqlite3_value*)pMem1, pColl->enc), - sqlite3ValueBytes((sqlite3_value*)pMem2, pColl->enc), - sqlite3ValueText((sqlite3_value*)pMem2, pColl->enc) - ); - sqlite3ValueBytes((sqlite3_value*)pMem1, origEnc); - sqlite3ValueText((sqlite3_value*)pMem1, origEnc); - sqlite3ValueBytes((sqlite3_value*)pMem2, origEnc); - sqlite3ValueText((sqlite3_value*)pMem2, origEnc); - return rc; - } - } - /* If a NULL pointer was passed as the collate function, fall through - ** to the blob case and use memcmp(). */ - } - - /* Both values must be blobs. Compare using memcmp(). */ - rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); - if( rc==0 ){ - rc = pMem1->n - pMem2->n; - } - return rc; -} - -/* -** Move data out of a btree key or data field and into a Mem structure. -** The data or key is taken from the entry that pCur is currently pointing -** to. offset and amt determine what portion of the data or key to retrieve. -** key is true to get the key or false to get data. The result is written -** into the pMem element. -** -** The pMem structure is assumed to be uninitialized. Any prior content -** is overwritten without being freed. -** -** If this routine fails for any reason (malloc returns NULL or unable -** to read from the disk) then the pMem is left in an inconsistent state. -*/ -int sqlite3VdbeMemFromBtree( - BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - int offset, /* Offset from the start of data to return bytes from. */ - int amt, /* Number of bytes to return. */ - int key, /* If true, retrieve from the btree key, not data. */ - Mem *pMem /* OUT: Return data in this Mem structure. */ -){ - char *zData; /* Data from the btree layer */ - int available; /* Number of bytes available on the local btree page */ - - if( key ){ - zData = (char *)sqlite3BtreeKeyFetch(pCur, &available); - }else{ - zData = (char *)sqlite3BtreeDataFetch(pCur, &available); - } - - pMem->n = amt; - if( offset+amt<=available ){ - pMem->z = &zData[offset]; - pMem->flags = MEM_Blob|MEM_Ephem; - }else{ - int rc; - if( amt>NBFS-2 ){ - zData = (char *)sqliteMallocRaw(amt+2); - if( !zData ){ - return SQLITE_NOMEM; - } - pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term; - pMem->xDel = 0; - }else{ - zData = &(pMem->zShort[0]); - pMem->flags = MEM_Blob|MEM_Short|MEM_Term; - } - pMem->z = zData; - pMem->enc = 0; - pMem->type = SQLITE_BLOB; - - if( key ){ - rc = sqlite3BtreeKey(pCur, offset, amt, zData); - }else{ - rc = sqlite3BtreeData(pCur, offset, amt, zData); - } - zData[amt] = 0; - zData[amt+1] = 0; - if( rc!=SQLITE_OK ){ - if( amt>NBFS ){ - sqliteFree(zData); - } - return rc; - } - } - - return SQLITE_OK; -} - -#ifndef NDEBUG -/* -** Perform various checks on the memory cell pMem. An assert() will -** fail if pMem is internally inconsistent. -*/ -void sqlite3VdbeMemSanity(Mem *pMem, u8 db_enc){ - int flags = pMem->flags; - assert( flags!=0 ); /* Must define some type */ - if( pMem->flags & (MEM_Str|MEM_Blob) ){ - int x = pMem->flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short); - assert( x!=0 ); /* Strings must define a string subtype */ - assert( (x & (x-1))==0 ); /* Only one string subtype can be defined */ - assert( pMem->z!=0 ); /* Strings must have a value */ - /* Mem.z points to Mem.zShort iff the subtype is MEM_Short */ - assert( (pMem->flags & MEM_Short)==0 || pMem->z==pMem->zShort ); - assert( (pMem->flags & MEM_Short)!=0 || pMem->z!=pMem->zShort ); - /* No destructor unless there is MEM_Dyn */ - assert( pMem->xDel==0 || (pMem->flags & MEM_Dyn)!=0 ); - - if( (flags & MEM_Str) ){ - assert( pMem->enc==SQLITE_UTF8 || - pMem->enc==SQLITE_UTF16BE || - pMem->enc==SQLITE_UTF16LE - ); - /* If the string is UTF-8 encoded and nul terminated, then pMem->n - ** must be the length of the string. (Later:) If the database file - ** has been corrupted, '\000' characters might have been inserted - ** into the middle of the string. In that case, the strlen() might - ** be less. - */ - if( pMem->enc==SQLITE_UTF8 && (flags & MEM_Term) ){ - assert( strlen(pMem->z)<=pMem->n ); - assert( pMem->z[pMem->n]==0 ); - } - } - }else{ - /* Cannot define a string subtype for non-string objects */ - assert( (pMem->flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short))==0 ); - assert( pMem->xDel==0 ); - } - /* MEM_Null excludes all other types */ - assert( (pMem->flags&(MEM_Str|MEM_Int|MEM_Real|MEM_Blob))==0 - || (pMem->flags&MEM_Null)==0 ); - if( (pMem->flags & (MEM_Int|MEM_Real))==(MEM_Int|MEM_Real) ){ - assert( pMem->r==pMem->i ); - } -} -#endif - -/* This function is only available internally, it is not part of the -** external API. It works in a similar way to sqlite3_value_text(), -** except the data returned is in the encoding specified by the second -** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or -** SQLITE_UTF8. -*/ -const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ - if( !pVal ) return 0; - assert( enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE || enc==SQLITE_UTF8); - - if( pVal->flags&MEM_Null ){ - return 0; - } - if( pVal->flags&MEM_Str ){ - sqlite3VdbeChangeEncoding(pVal, enc); - }else if( !(pVal->flags&MEM_Blob) ){ - sqlite3VdbeMemStringify(pVal, enc); - } - return (const void *)(pVal->z); -} - -/* -** Create a new sqlite3_value object. -*/ -sqlite3_value* sqlite3ValueNew(){ - Mem *p = sqliteMalloc(sizeof(*p)); - if( p ){ - p->flags = MEM_Null; - p->type = SQLITE_NULL; - } - return p; -} - -/* -** Change the string value of an sqlite3_value object -*/ -void sqlite3ValueSetStr( - sqlite3_value *v, - int n, - const void *z, - u8 enc, - void (*xDel)(void*) -){ - if( v ) sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel); -} - -/* -** Free an sqlite3_value object -*/ -void sqlite3ValueFree(sqlite3_value *v){ - if( !v ) return; - sqlite3ValueSetStr(v, 0, 0, SQLITE_UTF8, SQLITE_STATIC); - sqliteFree(v); -} - -/* -** Return the number of bytes in the sqlite3_value object assuming -** that it uses the encoding "enc" -*/ -int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ - Mem *p = (Mem*)pVal; - if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){ - return p->n; - } - return 0; -} diff --git a/kopete/plugins/statistics/sqlite/where.c b/kopete/plugins/statistics/sqlite/where.c deleted file mode 100644 index 08c174e9..00000000 --- a/kopete/plugins/statistics/sqlite/where.c +++ /dev/null @@ -1,1210 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This module contains C code that generates VDBE code used to process -** the WHERE clause of SQL statements. -** -** $Id$ -*/ -#include "sqliteInt.h" - -/* -** The query generator uses an array of instances of this structure to -** help it analyze the subexpressions of the WHERE clause. Each WHERE -** clause subexpression is separated from the others by an AND operator. -*/ -typedef struct ExprInfo ExprInfo; -struct ExprInfo { - Expr *p; /* Pointer to the subexpression */ - u8 indexable; /* True if this subexprssion is usable by an index */ - short int idxLeft; /* p->pLeft is a column in this table number. -1 if - ** p->pLeft is not the column of any table */ - short int idxRight; /* p->pRight is a column in this table number. -1 if - ** p->pRight is not the column of any table */ - unsigned prereqLeft; /* Bitmask of tables referenced by p->pLeft */ - unsigned prereqRight; /* Bitmask of tables referenced by p->pRight */ - unsigned prereqAll; /* Bitmask of tables referenced by p */ -}; - -/* -** An instance of the following structure keeps track of a mapping -** between VDBE cursor numbers and bitmasks. The VDBE cursor numbers -** are small integers contained in SrcList_item.iCursor and Expr.iTable -** fields. For any given WHERE clause, we want to track which cursors -** are being used, so we assign a single bit in a 32-bit word to track -** that cursor. Then a 32-bit integer is able to show the set of all -** cursors being used. -*/ -typedef struct ExprMaskSet ExprMaskSet; -struct ExprMaskSet { - int n; /* Number of assigned cursor values */ - int ix[31]; /* Cursor assigned to each bit */ -}; - -/* -** Determine the number of elements in an array. -*/ -#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0])) - -/* -** This routine is used to divide the WHERE expression into subexpressions -** separated by the AND operator. -** -** aSlot[] is an array of subexpressions structures. -** There are nSlot spaces left in this array. This routine attempts to -** split pExpr into subexpressions and fills aSlot[] with those subexpressions. -** The return value is the number of slots filled. -*/ -static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){ - int cnt = 0; - if( pExpr==0 || nSlot<1 ) return 0; - if( nSlot==1 || pExpr->op!=TK_AND ){ - aSlot[0].p = pExpr; - return 1; - } - if( pExpr->pLeft->op!=TK_AND ){ - aSlot[0].p = pExpr->pLeft; - cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight); - }else{ - cnt = exprSplit(nSlot, aSlot, pExpr->pLeft); - cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pRight); - } - return cnt; -} - -/* -** Initialize an expression mask set -*/ -#define initMaskSet(P) memset(P, 0, sizeof(*P)) - -/* -** Return the bitmask for the given cursor. Assign a new bitmask -** if this is the first time the cursor has been seen. -*/ -static int getMask(ExprMaskSet *pMaskSet, int iCursor){ - int i; - for(i=0; i<pMaskSet->n; i++){ - if( pMaskSet->ix[i]==iCursor ) return 1<<i; - } - if( i==pMaskSet->n && i<ARRAYSIZE(pMaskSet->ix) ){ - pMaskSet->n++; - pMaskSet->ix[i] = iCursor; - return 1<<i; - } - return 0; -} - -/* -** Destroy an expression mask set -*/ -#define freeMaskSet(P) /* NO-OP */ - -/* -** This routine walks (recursively) an expression tree and generates -** a bitmask indicating which tables are used in that expression -** tree. -** -** In order for this routine to work, the calling function must have -** previously invoked sqlite3ExprResolveIds() on the expression. See -** the header comment on that routine for additional information. -** The sqlite3ExprResolveIds() routines looks for column names and -** sets their opcodes to TK_COLUMN and their Expr.iTable fields to -** the VDBE cursor number of the table. -*/ -static int exprTableUsage(ExprMaskSet *pMaskSet, Expr *p){ - unsigned int mask = 0; - if( p==0 ) return 0; - if( p->op==TK_COLUMN ){ - mask = getMask(pMaskSet, p->iTable); - if( mask==0 ) mask = -1; - return mask; - } - if( p->pRight ){ - mask = exprTableUsage(pMaskSet, p->pRight); - } - if( p->pLeft ){ - mask |= exprTableUsage(pMaskSet, p->pLeft); - } - if( p->pList ){ - int i; - for(i=0; i<p->pList->nExpr; i++){ - mask |= exprTableUsage(pMaskSet, p->pList->a[i].pExpr); - } - } - return mask; -} - -/* -** Return TRUE if the given operator is one of the operators that is -** allowed for an indexable WHERE clause. The allowed operators are -** "=", "<", ">", "<=", ">=", and "IN". -*/ -static int allowedOp(int op){ - assert( TK_GT==TK_LE-1 && TK_LE==TK_LT-1 && TK_LT==TK_GE-1 && TK_EQ==TK_GT-1); - return op==TK_IN || (op>=TK_EQ && op<=TK_GE); -} - -/* -** Swap two integers. -*/ -#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} - -/* -** Return the index in the SrcList that uses cursor iCur. If iCur is -** used by the first entry in SrcList return 0. If iCur is used by -** the second entry return 1. And so forth. -** -** SrcList is the set of tables in the FROM clause in the order that -** they will be processed. The value returned here gives us an index -** of which tables will be processed first. -*/ -static int tableOrder(SrcList *pList, int iCur){ - int i; - for(i=0; i<pList->nSrc; i++){ - if( pList->a[i].iCursor==iCur ) return i; - } - return -1; -} - -/* -** The input to this routine is an ExprInfo structure with only the -** "p" field filled in. The job of this routine is to analyze the -** subexpression and populate all the other fields of the ExprInfo -** structure. -*/ -static void exprAnalyze(SrcList *pSrc, ExprMaskSet *pMaskSet, ExprInfo *pInfo){ - Expr *pExpr = pInfo->p; - pInfo->prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); - pInfo->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight); - pInfo->prereqAll = exprTableUsage(pMaskSet, pExpr); - pInfo->indexable = 0; - pInfo->idxLeft = -1; - pInfo->idxRight = -1; - if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){ - if( pExpr->pRight && pExpr->pRight->op==TK_COLUMN ){ - pInfo->idxRight = pExpr->pRight->iTable; - pInfo->indexable = 1; - } - if( pExpr->pLeft->op==TK_COLUMN ){ - pInfo->idxLeft = pExpr->pLeft->iTable; - pInfo->indexable = 1; - } - } - if( pInfo->indexable ){ - assert( pInfo->idxLeft!=pInfo->idxRight ); - - /* We want the expression to be of the form "X = expr", not "expr = X". - ** So flip it over if necessary. If the expression is "X = Y", then - ** we want Y to come from an earlier table than X. - ** - ** The collating sequence rule is to always choose the left expression. - ** So if we do a flip, we also have to move the collating sequence. - */ - if( tableOrder(pSrc,pInfo->idxLeft)<tableOrder(pSrc,pInfo->idxRight) ){ - assert( pExpr->op!=TK_IN ); - SWAP(CollSeq*,pExpr->pRight->pColl,pExpr->pLeft->pColl); - SWAP(Expr*,pExpr->pRight,pExpr->pLeft); - if( pExpr->op>=TK_GT ){ - assert( TK_LT==TK_GT+2 ); - assert( TK_GE==TK_LE+2 ); - assert( TK_GT>TK_EQ ); - assert( TK_GT<TK_LE ); - assert( pExpr->op>=TK_GT && pExpr->op<=TK_GE ); - pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT; - } - SWAP(unsigned, pInfo->prereqLeft, pInfo->prereqRight); - SWAP(short int, pInfo->idxLeft, pInfo->idxRight); - } - } - -} - -/* -** pOrderBy is an ORDER BY clause from a SELECT statement. pTab is the -** left-most table in the FROM clause of that same SELECT statement and -** the table has a cursor number of "base". -** -** This routine attempts to find an index for pTab that generates the -** correct record sequence for the given ORDER BY clause. The return value -** is a pointer to an index that does the job. NULL is returned if the -** table has no index that will generate the correct sort order. -** -** If there are two or more indices that generate the correct sort order -** and pPreferredIdx is one of those indices, then return pPreferredIdx. -** -** nEqCol is the number of columns of pPreferredIdx that are used as -** equality constraints. Any index returned must have exactly this same -** set of columns. The ORDER BY clause only matches index columns beyond the -** the first nEqCol columns. -** -** All terms of the ORDER BY clause must be either ASC or DESC. The -** *pbRev value is set to 1 if the ORDER BY clause is all DESC and it is -** set to 0 if the ORDER BY clause is all ASC. -*/ -static Index *findSortingIndex( - Parse *pParse, - Table *pTab, /* The table to be sorted */ - int base, /* Cursor number for pTab */ - ExprList *pOrderBy, /* The ORDER BY clause */ - Index *pPreferredIdx, /* Use this index, if possible and not NULL */ - int nEqCol, /* Number of index columns used with == constraints */ - int *pbRev /* Set to 1 if ORDER BY is DESC */ -){ - int i, j; - Index *pMatch; - Index *pIdx; - int sortOrder; - sqlite3 *db = pParse->db; - - assert( pOrderBy!=0 ); - assert( pOrderBy->nExpr>0 ); - sortOrder = pOrderBy->a[0].sortOrder; - for(i=0; i<pOrderBy->nExpr; i++){ - Expr *p; - if( pOrderBy->a[i].sortOrder!=sortOrder ){ - /* Indices can only be used if all ORDER BY terms are either - ** DESC or ASC. Indices cannot be used on a mixture. */ - return 0; - } - p = pOrderBy->a[i].pExpr; - if( p->op!=TK_COLUMN || p->iTable!=base ){ - /* Can not use an index sort on anything that is not a column in the - ** left-most table of the FROM clause */ - return 0; - } - } - - /* If we get this far, it means the ORDER BY clause consists only of - ** ascending columns in the left-most table of the FROM clause. Now - ** check for a matching index. - */ - pMatch = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nExpr = pOrderBy->nExpr; - if( pIdx->nColumn < nEqCol || pIdx->nColumn < nExpr ) continue; - for(i=j=0; i<nEqCol; i++){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[j].pExpr); - if( !pColl ) pColl = db->pDfltColl; - if( pPreferredIdx->aiColumn[i]!=pIdx->aiColumn[i] ) break; - if( pPreferredIdx->keyInfo.aColl[i]!=pIdx->keyInfo.aColl[i] ) break; - if( j<nExpr && - pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] && - pColl==pIdx->keyInfo.aColl[i] - ){ - j++; - } - } - if( i<nEqCol ) continue; - for(i=0; i+j<nExpr; i++){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[i+j].pExpr); - if( !pColl ) pColl = db->pDfltColl; - if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] || - pColl!=pIdx->keyInfo.aColl[i+nEqCol] ) break; - } - if( i+j>=nExpr ){ - pMatch = pIdx; - if( pIdx==pPreferredIdx ) break; - } - } - if( pMatch && pbRev ){ - *pbRev = sortOrder==SQLITE_SO_DESC; - } - return pMatch; -} - -/* -** Disable a term in the WHERE clause. Except, do not disable the term -** if it controls a LEFT OUTER JOIN and it did not originate in the ON -** or USING clause of that join. -** -** Consider the term t2.z='ok' in the following queries: -** -** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok' -** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok' -** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok' -** -** The t2.z='ok' is disabled in the in (2) because it did not originate -** in the ON clause. The term is disabled in (3) because it is not part -** of a LEFT OUTER JOIN. In (1), the term is not disabled. -** -** Disabling a term causes that term to not be tested in the inner loop -** of the join. Disabling is an optimization. We would get the correct -** results if nothing were ever disabled, but joins might run a little -** slower. The trick is to disable as much as we can without disabling -** too much. If we disabled in (1), we'd get the wrong answer. -** See ticket #813. -*/ -static void disableTerm(WhereLevel *pLevel, Expr **ppExpr){ - Expr *pExpr = *ppExpr; - if( pLevel->iLeftJoin==0 || ExprHasProperty(pExpr, EP_FromJoin) ){ - *ppExpr = 0; - } -} - -/* -** Generate code that builds a probe for an index. Details: -** -** * Check the top nColumn entries on the stack. If any -** of those entries are NULL, jump immediately to brk, -** which is the loop exit, since no index entry will match -** if any part of the key is NULL. -** -** * Construct a probe entry from the top nColumn entries in -** the stack with affinities appropriate for index pIdx. -*/ -static void buildIndexProbe(Vdbe *v, int nColumn, int brk, Index *pIdx){ - sqlite3VdbeAddOp(v, OP_NotNull, -nColumn, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, brk); - sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); - sqlite3IndexAffinityStr(v, pIdx); -} - -/* -** Generate code for an equality term of the WHERE clause. An equality -** term can be either X=expr or X IN (...). pTerm is the X. -*/ -static void codeEqualityTerm( - Parse *pParse, /* The parsing context */ - ExprInfo *pTerm, /* The term of the WHERE clause to be coded */ - int brk, /* Jump here to abandon the loop */ - WhereLevel *pLevel /* When level of the FROM clause we are working on */ -){ - Expr *pX = pTerm->p; - if( pX->op!=TK_IN ){ - assert( pX->op==TK_EQ ); - sqlite3ExprCode(pParse, pX->pRight); - }else{ - int iTab = pX->iTable; - Vdbe *v = pParse->pVdbe; - sqlite3VdbeAddOp(v, OP_Rewind, iTab, brk); - sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1); - pLevel->inP2 = sqlite3VdbeAddOp(v, OP_IdxColumn, iTab, 0); - pLevel->inOp = OP_Next; - pLevel->inP1 = iTab; - } - disableTerm(pLevel, &pTerm->p); -} - - -/* -** Generate the beginning of the loop used for WHERE clause processing. -** The return value is a pointer to an (opaque) structure that contains -** information needed to terminate the loop. Later, the calling routine -** should invoke sqlite3WhereEnd() with the return value of this function -** in order to complete the WHERE clause processing. -** -** If an error occurs, this routine returns NULL. -** -** The basic idea is to do a nested loop, one loop for each table in -** the FROM clause of a select. (INSERT and UPDATE statements are the -** same as a SELECT with only a single table in the FROM clause.) For -** example, if the SQL is this: -** -** SELECT * FROM t1, t2, t3 WHERE ...; -** -** Then the code generated is conceptually like the following: -** -** foreach row1 in t1 do \ Code generated -** foreach row2 in t2 do |-- by sqlite3WhereBegin() -** foreach row3 in t3 do / -** ... -** end \ Code generated -** end |-- by sqlite3WhereEnd() -** end / -** -** There are Btree cursors associated with each table. t1 uses cursor -** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. -** And so forth. This routine generates code to open those VDBE cursors -** and sqlite3WhereEnd() generates the code to close them. -** -** If the WHERE clause is empty, the foreach loops must each scan their -** entire tables. Thus a three-way join is an O(N^3) operation. But if -** the tables have indices and there are terms in the WHERE clause that -** refer to those indices, a complete table scan can be avoided and the -** code will run much faster. Most of the work of this routine is checking -** to see if there are indices that can be used to speed up the loop. -** -** Terms of the WHERE clause are also used to limit which rows actually -** make it to the "..." in the middle of the loop. After each "foreach", -** terms of the WHERE clause that use only terms in that loop and outer -** loops are evaluated and if false a jump is made around all subsequent -** inner loops (or around the "..." if the test occurs within the inner- -** most loop) -** -** OUTER JOINS -** -** An outer join of tables t1 and t2 is conceptally coded as follows: -** -** foreach row1 in t1 do -** flag = 0 -** foreach row2 in t2 do -** start: -** ... -** flag = 1 -** end -** if flag==0 then -** move the row2 cursor to a null row -** goto start -** fi -** end -** -** ORDER BY CLAUSE PROCESSING -** -** *ppOrderBy is a pointer to the ORDER BY clause of a SELECT statement, -** if there is one. If there is no ORDER BY clause or if this routine -** is called from an UPDATE or DELETE statement, then ppOrderBy is NULL. -** -** If an index can be used so that the natural output order of the table -** scan is correct for the ORDER BY clause, then that index is used and -** *ppOrderBy is set to NULL. This is an optimization that prevents an -** unnecessary sort of the result set if an index appropriate for the -** ORDER BY clause already exists. -** -** If the where clause loops cannot be arranged to provide the correct -** output order, then the *ppOrderBy is unchanged. -*/ -WhereInfo *sqlite3WhereBegin( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* A list of all tables to be scanned */ - Expr *pWhere, /* The WHERE clause */ - int pushKey, /* If TRUE, leave the table key on the stack */ - ExprList **ppOrderBy /* An ORDER BY clause, or NULL */ -){ - int i; /* Loop counter */ - WhereInfo *pWInfo; /* Will become the return value of this function */ - Vdbe *v = pParse->pVdbe; /* The virtual database engine */ - int brk, cont = 0; /* Addresses used during code generation */ - int nExpr; /* Number of subexpressions in the WHERE clause */ - int loopMask; /* One bit set for each outer loop */ - int haveKey = 0; /* True if KEY is on the stack */ - ExprInfo *pTerm; /* A single term in the WHERE clause; ptr to aExpr[] */ - ExprMaskSet maskSet; /* The expression mask set */ - int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */ - int iDirectLt[32]; /* Term of the form ROWID<X or ROWID<=X */ - int iDirectGt[32]; /* Term of the form ROWID>X or ROWID>=X */ - ExprInfo aExpr[101]; /* The WHERE clause is divided into these terms */ - - /* pushKey is only allowed if there is a single table (as in an INSERT or - ** UPDATE statement) - */ - assert( pushKey==0 || pTabList->nSrc==1 ); - - /* Split the WHERE clause into separate subexpressions where each - ** subexpression is separated by an AND operator. If the aExpr[] - ** array fills up, the last entry might point to an expression which - ** contains additional unfactored AND operators. - */ - initMaskSet(&maskSet); - memset(aExpr, 0, sizeof(aExpr)); - nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere); - if( nExpr==ARRAYSIZE(aExpr) ){ - sqlite3ErrorMsg(pParse, "WHERE clause too complex - no more " - "than %d terms allowed", (int)ARRAYSIZE(aExpr)-1); - return 0; - } - - /* Allocate and initialize the WhereInfo structure that will become the - ** return value. - */ - pWInfo = sqliteMalloc( sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel)); - if( sqlite3_malloc_failed ){ - /* sqliteFree(pWInfo); // Leak memory when malloc fails */ - return 0; - } - pWInfo->pParse = pParse; - pWInfo->pTabList = pTabList; - pWInfo->iBreak = sqlite3VdbeMakeLabel(v); - - /* Special case: a WHERE clause that is constant. Evaluate the - ** expression and either jump over all of the code or fall thru. - */ - if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstant(pWhere)) ){ - sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1); - pWhere = 0; - } - - /* Analyze all of the subexpressions. - */ - for(pTerm=aExpr, i=0; i<nExpr; i++, pTerm++){ - TriggerStack *pStack; - exprAnalyze(pTabList, &maskSet, pTerm); - - /* If we are executing a trigger body, remove all references to - ** new.* and old.* tables from the prerequisite masks. - */ - if( (pStack = pParse->trigStack)!=0 ){ - int x; - if( (x=pStack->newIdx) >= 0 ){ - int mask = ~getMask(&maskSet, x); - pTerm->prereqRight &= mask; - pTerm->prereqLeft &= mask; - pTerm->prereqAll &= mask; - } - if( (x=pStack->oldIdx) >= 0 ){ - int mask = ~getMask(&maskSet, x); - pTerm->prereqRight &= mask; - pTerm->prereqLeft &= mask; - pTerm->prereqAll &= mask; - } - } - } - - /* Figure out what index to use (if any) for each nested loop. - ** Make pWInfo->a[i].pIdx point to the index to use for the i-th nested - ** loop where i==0 is the outer loop and i==pTabList->nSrc-1 is the inner - ** loop. - ** - ** If terms exist that use the ROWID of any table, then set the - ** iDirectEq[], iDirectLt[], or iDirectGt[] elements for that table - ** to the index of the term containing the ROWID. We always prefer - ** to use a ROWID which can directly access a table rather than an - ** index which requires reading an index first to get the rowid then - ** doing a second read of the actual database table. - ** - ** Actually, if there are more than 32 tables in the join, only the - ** first 32 tables are candidates for indices. This is (again) due - ** to the limit of 32 bits in an integer bitmask. - */ - loopMask = 0; - for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++){ - int j; - WhereLevel *pLevel = &pWInfo->a[i]; - int iCur = pTabList->a[i].iCursor; /* The cursor for this table */ - int mask = getMask(&maskSet, iCur); /* Cursor mask for this table */ - Table *pTab = pTabList->a[i].pTab; - Index *pIdx; - Index *pBestIdx = 0; - int bestScore = 0; - - /* Check to see if there is an expression that uses only the - ** ROWID field of this table. For terms of the form ROWID==expr - ** set iDirectEq[i] to the index of the term. For terms of the - ** form ROWID<expr or ROWID<=expr set iDirectLt[i] to the term index. - ** For terms like ROWID>expr or ROWID>=expr set iDirectGt[i]. - ** - ** (Added:) Treat ROWID IN expr like ROWID=expr. - */ - pLevel->iCur = -1; - iDirectEq[i] = -1; - iDirectLt[i] = -1; - iDirectGt[i] = -1; - for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){ - Expr *pX = pTerm->p; - if( pTerm->idxLeft==iCur && pX->pLeft->iColumn<0 - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight ){ - switch( pX->op ){ - case TK_IN: - case TK_EQ: iDirectEq[i] = j; break; - case TK_LE: - case TK_LT: iDirectLt[i] = j; break; - case TK_GE: - case TK_GT: iDirectGt[i] = j; break; - } - } - } - if( iDirectEq[i]>=0 ){ - loopMask |= mask; - pLevel->pIdx = 0; - continue; - } - - /* Do a search for usable indices. Leave pBestIdx pointing to - ** the "best" index. pBestIdx is left set to NULL if no indices - ** are usable. - ** - ** The best index is determined as follows. For each of the - ** left-most terms that is fixed by an equality operator, add - ** 8 to the score. The right-most term of the index may be - ** constrained by an inequality. Add 1 if for an "x<..." constraint - ** and add 2 for an "x>..." constraint. Chose the index that - ** gives the best score. - ** - ** This scoring system is designed so that the score can later be - ** used to determine how the index is used. If the score&7 is 0 - ** then all constraints are equalities. If score&1 is not 0 then - ** there is an inequality used as a termination key. (ex: "x<...") - ** If score&2 is not 0 then there is an inequality used as the - ** start key. (ex: "x>..."). A score or 4 is the special case - ** of an IN operator constraint. (ex: "x IN ..."). - ** - ** The IN operator (as in "<expr> IN (...)") is treated the same as - ** an equality comparison except that it can only be used on the - ** left-most column of an index and other terms of the WHERE clause - ** cannot be used in conjunction with the IN operator to help satisfy - ** other columns of the index. - */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int eqMask = 0; /* Index columns covered by an x=... term */ - int ltMask = 0; /* Index columns covered by an x<... term */ - int gtMask = 0; /* Index columns covered by an x>... term */ - int inMask = 0; /* Index columns covered by an x IN .. term */ - int nEq, m, score; - - if( pIdx->nColumn>32 ) continue; /* Ignore indices too many columns */ - for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){ - Expr *pX = pTerm->p; - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pX->pLeft); - if( !pColl && pX->pRight ){ - pColl = sqlite3ExprCollSeq(pParse, pX->pRight); - } - if( !pColl ){ - pColl = pParse->db->pDfltColl; - } - if( pTerm->idxLeft==iCur - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight ){ - int iColumn = pX->pLeft->iColumn; - int k; - char idxaff = pIdx->pTable->aCol[iColumn].affinity; - for(k=0; k<pIdx->nColumn; k++){ - /* If the collating sequences or affinities don't match, - ** ignore this index. */ - if( pColl!=pIdx->keyInfo.aColl[k] ) continue; - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; - if( pIdx->aiColumn[k]==iColumn ){ - switch( pX->op ){ - case TK_IN: { - if( k==0 ) inMask |= 1; - break; - } - case TK_EQ: { - eqMask |= 1<<k; - break; - } - case TK_LE: - case TK_LT: { - ltMask |= 1<<k; - break; - } - case TK_GE: - case TK_GT: { - gtMask |= 1<<k; - break; - } - default: { - /* CANT_HAPPEN */ - assert( 0 ); - break; - } - } - break; - } - } - } - } - - /* The following loop ends with nEq set to the number of columns - ** on the left of the index with == constraints. - */ - for(nEq=0; nEq<pIdx->nColumn; nEq++){ - m = (1<<(nEq+1))-1; - if( (m & eqMask)!=m ) break; - } - score = nEq*8; /* Base score is 8 times number of == constraints */ - m = 1<<nEq; - if( m & ltMask ) score++; /* Increase score for a < constraint */ - if( m & gtMask ) score+=2; /* Increase score for a > constraint */ - if( score==0 && inMask ) score = 4; /* Default score for IN constraint */ - if( score>bestScore ){ - pBestIdx = pIdx; - bestScore = score; - } - } - pLevel->pIdx = pBestIdx; - pLevel->score = bestScore; - pLevel->bRev = 0; - loopMask |= mask; - if( pBestIdx ){ - pLevel->iCur = pParse->nTab++; - } - } - - /* Check to see if the ORDER BY clause is or can be satisfied by the - ** use of an index on the first table. - */ - if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ){ - Index *pSortIdx; - Index *pIdx; - Table *pTab; - int bRev = 0; - - pTab = pTabList->a[0].pTab; - pIdx = pWInfo->a[0].pIdx; - if( pIdx && pWInfo->a[0].score==4 ){ - /* If there is already an IN index on the left-most table, - ** it will not give the correct sort order. - ** So, pretend that no suitable index is found. - */ - pSortIdx = 0; - }else if( iDirectEq[0]>=0 || iDirectLt[0]>=0 || iDirectGt[0]>=0 ){ - /* If the left-most column is accessed using its ROWID, then do - ** not try to sort by index. - */ - pSortIdx = 0; - }else{ - int nEqCol = (pWInfo->a[0].score+4)/8; - pSortIdx = findSortingIndex(pParse, pTab, pTabList->a[0].iCursor, - *ppOrderBy, pIdx, nEqCol, &bRev); - } - if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ){ - if( pIdx==0 ){ - pWInfo->a[0].pIdx = pSortIdx; - pWInfo->a[0].iCur = pParse->nTab++; - } - pWInfo->a[0].bRev = bRev; - *ppOrderBy = 0; - } - } - - /* Open all tables in the pTabList and all indices used by those tables. - */ - sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ - for(i=0; i<pTabList->nSrc; i++){ - Table *pTab; - Index *pIx; - - pTab = pTabList->a[i].pTab; - if( pTab->isTransient || pTab->pSelect ) continue; - sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab); - sqlite3CodeVerifySchema(pParse, pTab->iDb); - if( (pIx = pWInfo->a[i].pIdx)!=0 ){ - sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0); - sqlite3VdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum, - (char*)&pIx->keyInfo, P3_KEYINFO); - } - } - - /* Generate the code to do the search - */ - loopMask = 0; - for(i=0; i<pTabList->nSrc; i++){ - int j, k; - int iCur = pTabList->a[i].iCursor; - Index *pIdx; - WhereLevel *pLevel = &pWInfo->a[i]; - - /* If this is the right table of a LEFT OUTER JOIN, allocate and - ** initialize a memory cell that records if this table matches any - ** row of the left table of the join. - */ - if( i>0 && (pTabList->a[i-1].jointype & JT_LEFT)!=0 ){ - if( !pParse->nMem ) pParse->nMem++; - pLevel->iLeftJoin = pParse->nMem++; - sqlite3VdbeAddOp(v, OP_String8, 0, 0); - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1); - VdbeComment((v, "# init LEFT JOIN no-match flag")); - } - - pIdx = pLevel->pIdx; - pLevel->inOp = OP_Noop; - if( i<ARRAYSIZE(iDirectEq) && (k = iDirectEq[i])>=0 ){ - /* Case 1: We can directly reference a single row using an - ** equality comparison against the ROWID field. Or - ** we reference multiple rows using a "rowid IN (...)" - ** construct. - */ - assert( k<nExpr ); - pTerm = &aExpr[k]; - assert( pTerm->p!=0 ); - assert( pTerm->idxLeft==iCur ); - brk = pLevel->brk = sqlite3VdbeMakeLabel(v); - codeEqualityTerm(pParse, pTerm, brk, pLevel); - cont = pLevel->cont = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_MustBeInt, 1, brk); - haveKey = 0; - sqlite3VdbeAddOp(v, OP_NotExists, iCur, brk); - pLevel->op = OP_Noop; - }else if( pIdx!=0 && pLevel->score>0 && pLevel->score%4==0 ){ - /* Case 2: There is an index and all terms of the WHERE clause that - ** refer to the index use the "==" or "IN" operators. - */ - int start; - int nColumn = (pLevel->score+4)/8; - brk = pLevel->brk = sqlite3VdbeMakeLabel(v); - - /* For each column of the index, find the term of the WHERE clause that - ** constraints that column. If the WHERE clause term is X=expr, then - ** evaluation expr and leave the result on the stack */ - for(j=0; j<nColumn; j++){ - for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){ - Expr *pX = pTerm->p; - if( pX==0 ) continue; - if( pTerm->idxLeft==iCur - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight - && pX->pLeft->iColumn==pIdx->aiColumn[j] - ){ - char idxaff = pIdx->pTable->aCol[pX->pLeft->iColumn].affinity; - if( sqlite3IndexAffinityOk(pX, idxaff) ){ - codeEqualityTerm(pParse, pTerm, brk, pLevel); - break; - } - } - } - } - pLevel->iMem = pParse->nMem++; - cont = pLevel->cont = sqlite3VdbeMakeLabel(v); - buildIndexProbe(v, nColumn, brk, pIdx); - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0); - - /* Generate code (1) to move to the first matching element of the table. - ** Then generate code (2) that jumps to "brk" after the cursor is past - ** the last matching element of the table. The code (1) is executed - ** once to initialize the search, the code (2) is executed before each - ** iteration of the scan to see if the scan has finished. */ - if( pLevel->bRev ){ - /* Scan in reverse order */ - sqlite3VdbeAddOp(v, OP_MoveLe, pLevel->iCur, brk); - start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); - sqlite3VdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk); - pLevel->op = OP_Prev; - }else{ - /* Scan in the forward order */ - sqlite3VdbeAddOp(v, OP_MoveGe, pLevel->iCur, brk); - start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); - sqlite3VdbeOp3(v, OP_IdxGE, pLevel->iCur, brk, "+", P3_STATIC); - pLevel->op = OP_Next; - } - sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0); - sqlite3VdbeAddOp(v, OP_IdxIsNull, nColumn, cont); - sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0); - if( i==pTabList->nSrc-1 && pushKey ){ - haveKey = 1; - }else{ - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - haveKey = 0; - } - pLevel->p1 = pLevel->iCur; - pLevel->p2 = start; - }else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ){ - /* Case 3: We have an inequality comparison against the ROWID field. - */ - int testOp = OP_Noop; - int start; - - brk = pLevel->brk = sqlite3VdbeMakeLabel(v); - cont = pLevel->cont = sqlite3VdbeMakeLabel(v); - if( iDirectGt[i]>=0 ){ - Expr *pX; - k = iDirectGt[i]; - assert( k<nExpr ); - pTerm = &aExpr[k]; - pX = pTerm->p; - assert( pX!=0 ); - assert( pTerm->idxLeft==iCur ); - sqlite3ExprCode(pParse, pX->pRight); - sqlite3VdbeAddOp(v, OP_ForceInt, pX->op==TK_LT || pX->op==TK_GT, brk); - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, brk); - disableTerm(pLevel, &pTerm->p); - }else{ - sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk); - } - if( iDirectLt[i]>=0 ){ - Expr *pX; - k = iDirectLt[i]; - assert( k<nExpr ); - pTerm = &aExpr[k]; - pX = pTerm->p; - assert( pX!=0 ); - assert( pTerm->idxLeft==iCur ); - sqlite3ExprCode(pParse, pX->pRight); - pLevel->iMem = pParse->nMem++; - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); - if( pX->op==TK_LT || pX->op==TK_GT ){ - testOp = OP_Ge; - }else{ - testOp = OP_Gt; - } - disableTerm(pLevel, &pTerm->p); - } - start = sqlite3VdbeCurrentAddr(v); - pLevel->op = OP_Next; - pLevel->p1 = iCur; - pLevel->p2 = start; - if( testOp!=OP_Noop ){ - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); - sqlite3VdbeAddOp(v, testOp, 0, brk); - } - haveKey = 0; - }else if( pIdx==0 ){ - /* Case 4: There is no usable index. We must do a complete - ** scan of the entire database table. - */ - int start; - - brk = pLevel->brk = sqlite3VdbeMakeLabel(v); - cont = pLevel->cont = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk); - start = sqlite3VdbeCurrentAddr(v); - pLevel->op = OP_Next; - pLevel->p1 = iCur; - pLevel->p2 = start; - haveKey = 0; - }else{ - /* Case 5: The WHERE clause term that refers to the right-most - ** column of the index is an inequality. For example, if - ** the index is on (x,y,z) and the WHERE clause is of the - ** form "x=5 AND y<10" then this case is used. Only the - ** right-most column can be an inequality - the rest must - ** use the "==" operator. - ** - ** This case is also used when there are no WHERE clause - ** constraints but an index is selected anyway, in order - ** to force the output order to conform to an ORDER BY. - */ - int score = pLevel->score; - int nEqColumn = score/8; - int start; - int leFlag=0, geFlag=0; - int testOp; - - /* Evaluate the equality constraints - */ - for(j=0; j<nEqColumn; j++){ - int iIdxCol = pIdx->aiColumn[j]; - for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){ - Expr *pX = pTerm->p; - if( pX==0 ) continue; - if( pTerm->idxLeft==iCur - && pX->op==TK_EQ - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight - && pX->pLeft->iColumn==iIdxCol - ){ - sqlite3ExprCode(pParse, pX->pRight); - disableTerm(pLevel, &pTerm->p); - break; - } - } - } - - /* Duplicate the equality term values because they will all be - ** used twice: once to make the termination key and once to make the - ** start key. - */ - for(j=0; j<nEqColumn; j++){ - sqlite3VdbeAddOp(v, OP_Dup, nEqColumn-1, 0); - } - - /* Labels for the beginning and end of the loop - */ - cont = pLevel->cont = sqlite3VdbeMakeLabel(v); - brk = pLevel->brk = sqlite3VdbeMakeLabel(v); - - /* Generate the termination key. This is the key value that - ** will end the search. There is no termination key if there - ** are no equality terms and no "X<..." term. - ** - ** 2002-Dec-04: On a reverse-order scan, the so-called "termination" - ** key computed here really ends up being the start key. - */ - if( (score & 1)!=0 ){ - for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){ - Expr *pX = pTerm->p; - if( pX==0 ) continue; - if( pTerm->idxLeft==iCur - && (pX->op==TK_LT || pX->op==TK_LE) - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight - && pX->pLeft->iColumn==pIdx->aiColumn[j] - ){ - sqlite3ExprCode(pParse, pX->pRight); - leFlag = pX->op==TK_LE; - disableTerm(pLevel, &pTerm->p); - break; - } - } - testOp = OP_IdxGE; - }else{ - testOp = nEqColumn>0 ? OP_IdxGE : OP_Noop; - leFlag = 1; - } - if( testOp!=OP_Noop ){ - int nCol = nEqColumn + (score & 1); - pLevel->iMem = pParse->nMem++; - buildIndexProbe(v, nCol, brk, pIdx); - if( pLevel->bRev ){ - int op = leFlag ? OP_MoveLe : OP_MoveLt; - sqlite3VdbeAddOp(v, op, pLevel->iCur, brk); - }else{ - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); - } - }else if( pLevel->bRev ){ - sqlite3VdbeAddOp(v, OP_Last, pLevel->iCur, brk); - } - - /* Generate the start key. This is the key that defines the lower - ** bound on the search. There is no start key if there are no - ** equality terms and if there is no "X>..." term. In - ** that case, generate a "Rewind" instruction in place of the - ** start key search. - ** - ** 2002-Dec-04: In the case of a reverse-order search, the so-called - ** "start" key really ends up being used as the termination key. - */ - if( (score & 2)!=0 ){ - for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){ - Expr *pX = pTerm->p; - if( pX==0 ) continue; - if( pTerm->idxLeft==iCur - && (pX->op==TK_GT || pX->op==TK_GE) - && (pTerm->prereqRight & loopMask)==pTerm->prereqRight - && pX->pLeft->iColumn==pIdx->aiColumn[j] - ){ - sqlite3ExprCode(pParse, pX->pRight); - geFlag = pX->op==TK_GE; - disableTerm(pLevel, &pTerm->p); - break; - } - } - }else{ - geFlag = 1; - } - if( nEqColumn>0 || (score&2)!=0 ){ - int nCol = nEqColumn + ((score&2)!=0); - buildIndexProbe(v, nCol, brk, pIdx); - if( pLevel->bRev ){ - pLevel->iMem = pParse->nMem++; - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); - testOp = OP_IdxLT; - }else{ - int op = geFlag ? OP_MoveGe : OP_MoveGt; - sqlite3VdbeAddOp(v, op, pLevel->iCur, brk); - } - }else if( pLevel->bRev ){ - testOp = OP_Noop; - }else{ - sqlite3VdbeAddOp(v, OP_Rewind, pLevel->iCur, brk); - } - - /* Generate the the top of the loop. If there is a termination - ** key we have to test for that key and abort at the top of the - ** loop. - */ - start = sqlite3VdbeCurrentAddr(v); - if( testOp!=OP_Noop ){ - sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); - sqlite3VdbeAddOp(v, testOp, pLevel->iCur, brk); - if( (leFlag && !pLevel->bRev) || (!geFlag && pLevel->bRev) ){ - sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC); - } - } - sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0); - sqlite3VdbeAddOp(v, OP_IdxIsNull, nEqColumn + (score & 1), cont); - sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0); - if( i==pTabList->nSrc-1 && pushKey ){ - haveKey = 1; - }else{ - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - haveKey = 0; - } - - /* Record the instruction used to terminate the loop. - */ - pLevel->op = pLevel->bRev ? OP_Prev : OP_Next; - pLevel->p1 = pLevel->iCur; - pLevel->p2 = start; - } - loopMask |= getMask(&maskSet, iCur); - - /* Insert code to test every subexpression that can be completely - ** computed using the current set of tables. - */ - for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){ - if( pTerm->p==0 ) continue; - if( (pTerm->prereqAll & loopMask)!=pTerm->prereqAll ) continue; - if( pLevel->iLeftJoin && !ExprHasProperty(pTerm->p,EP_FromJoin) ){ - continue; - } - if( haveKey ){ - haveKey = 0; - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - } - sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1); - pTerm->p = 0; - } - brk = cont; - - /* For a LEFT OUTER JOIN, generate code that will record the fact that - ** at least one row of the right table has matched the left table. - */ - if( pLevel->iLeftJoin ){ - pLevel->top = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp(v, OP_Integer, 1, 0); - sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1); - VdbeComment((v, "# record LEFT JOIN hit")); - for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){ - if( pTerm->p==0 ) continue; - if( (pTerm->prereqAll & loopMask)!=pTerm->prereqAll ) continue; - if( haveKey ){ - /* Cannot happen. "haveKey" can only be true if pushKey is true - ** an pushKey can only be true for DELETE and UPDATE and there are - ** no outer joins with DELETE and UPDATE. - */ - haveKey = 0; - sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); - } - sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1); - pTerm->p = 0; - } - } - } - pWInfo->iContinue = cont; - if( pushKey && !haveKey ){ - sqlite3VdbeAddOp(v, OP_Recno, pTabList->a[0].iCursor, 0); - } - freeMaskSet(&maskSet); - return pWInfo; -} - -/* -** Generate the end of the WHERE loop. See comments on -** sqlite3WhereBegin() for additional information. -*/ -void sqlite3WhereEnd(WhereInfo *pWInfo){ - Vdbe *v = pWInfo->pParse->pVdbe; - int i; - WhereLevel *pLevel; - SrcList *pTabList = pWInfo->pTabList; - - for(i=pTabList->nSrc-1; i>=0; i--){ - pLevel = &pWInfo->a[i]; - sqlite3VdbeResolveLabel(v, pLevel->cont); - if( pLevel->op!=OP_Noop ){ - sqlite3VdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2); - } - sqlite3VdbeResolveLabel(v, pLevel->brk); - if( pLevel->inOp!=OP_Noop ){ - sqlite3VdbeAddOp(v, pLevel->inOp, pLevel->inP1, pLevel->inP2); - } - if( pLevel->iLeftJoin ){ - int addr; - addr = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0); - sqlite3VdbeAddOp(v, OP_NotNull, 1, addr+4 + (pLevel->iCur>=0)); - sqlite3VdbeAddOp(v, OP_NullRow, pTabList->a[i].iCursor, 0); - if( pLevel->iCur>=0 ){ - sqlite3VdbeAddOp(v, OP_NullRow, pLevel->iCur, 0); - } - sqlite3VdbeAddOp(v, OP_Goto, 0, pLevel->top); - } - } - sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - for(i=0; i<pTabList->nSrc; i++){ - Table *pTab = pTabList->a[i].pTab; - assert( pTab!=0 ); - if( pTab->isTransient || pTab->pSelect ) continue; - pLevel = &pWInfo->a[i]; - sqlite3VdbeAddOp(v, OP_Close, pTabList->a[i].iCursor, 0); - if( pLevel->pIdx!=0 ){ - sqlite3VdbeAddOp(v, OP_Close, pLevel->iCur, 0); - } - } - sqliteFree(pWInfo); - return; -} diff --git a/kopete/plugins/statistics/statisticsdb.cpp b/kopete/plugins/statistics/statisticsdb.cpp index c0a41232..8f25623e 100644 --- a/kopete/plugins/statistics/statisticsdb.cpp +++ b/kopete/plugins/statistics/statisticsdb.cpp @@ -16,7 +16,7 @@ #include <tqfile.h> -#include "sqlite/sqlite3.h" +#include "sqlite3.h" #include <kgenericfactory.h> #include <kaboutdata.h> diff --git a/kopete/plugins/texteffect/CMakeLists.txt b/kopete/plugins/texteffect/CMakeLists.txt new file mode 100644 index 00000000..e88a0e34 --- /dev/null +++ b/kopete/plugins/texteffect/CMakeLists.txt @@ -0,0 +1,51 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_texteffect.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_texteffect_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kopete_texteffect (module) ################ + +tde_add_kpart( kopete_texteffect AUTOMOC + SOURCES + texteffectplugin.cpp texteffectconfig.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_texteffect (module) ############ + +tde_add_kpart( kcm_kopete_texteffect AUTOMOC + SOURCES + texteffectconfig.cpp texteffectprefs.ui texteffectpreferences.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/texteffect/icons/CMakeLists.txt b/kopete/plugins/texteffect/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/plugins/texteffect/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/plugins/translator/CMakeLists.txt b/kopete/plugins/translator/CMakeLists.txt new file mode 100644 index 00000000..5dd274db --- /dev/null +++ b/kopete/plugins/translator/CMakeLists.txt @@ -0,0 +1,51 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_translator.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_translator_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) +install( FILES translatorui.rc translatorchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_translator ) + + +##### kopete_translator (module) ################ + +tde_add_kpart( kopete_translator AUTOMOC + SOURCES + translatordialog.cpp translatorguiclient.cpp + translatorlanguages.cpp translatorplugin.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_translator (module) ############ + +tde_add_kpart( kcm_kopete_translator AUTOMOC + SOURCES + translatorprefsbase.ui translatorprefs.cpp + translatorlanguages.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/translator/translatorguiclient.cpp b/kopete/plugins/translator/translatorguiclient.cpp index 6af66f37..ef5ac9ac 100644 --- a/kopete/plugins/translator/translatorguiclient.cpp +++ b/kopete/plugins/translator/translatorguiclient.cpp @@ -23,7 +23,7 @@ #include <kaction.h> #include <klocale.h> -#include "kopetemessagemanager.h" +#include "kopetechatsession.h" #include "kopeteview.h" #include "kopetecontact.h" #include "kopetemetacontact.h" diff --git a/kopete/plugins/translator/translatorplugin.cpp b/kopete/plugins/translator/translatorplugin.cpp index 0767e24e..9bff86f2 100644 --- a/kopete/plugins/translator/translatorplugin.cpp +++ b/kopete/plugins/translator/translatorplugin.cpp @@ -35,7 +35,7 @@ #include "kopetemetacontact.h" #include "kopetecontactlist.h" -#include "kopetemessagemanagerfactory.h" +#include "kopetechatsessionmanager.h" #include "kopetecontact.h" #include "translatorplugin.h" diff --git a/kopete/plugins/webpresence/CMakeLists.txt b/kopete/plugins/webpresence/CMakeLists.txt new file mode 100644 index 00000000..bcba1176 --- /dev/null +++ b/kopete/plugins/webpresence/CMakeLists.txt @@ -0,0 +1,59 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_webpresence.desktop + DESTINATION ${SERVICES_INSTALL_DIR} ) + +install( FILES + kopete_webpresence_config.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + +install( FILES + webpresence_html.xsl webpresence_html_images.xsl + webpresence_xhtml.xsl webpresence_xhtml_images.xsl + DESTINATION ${DATA_INSTALL_DIR}/kopete/webpresence ) + + +##### kopete_webpresence (module) ############### + +tde_add_kpart( kopete_webpresence AUTOMOC + SOURCES + webpresenceplugin.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_webpresence (module) ########### + +tde_add_kpart( kcm_kopete_webpresence AUTOMOC + SOURCES + webpresencepreferences.cpp webpresenceprefs.ui + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/CMakeLists.txt b/kopete/protocols/CMakeLists.txt new file mode 100644 index 00000000..14127078 --- /dev/null +++ b/kopete/protocols/CMakeLists.txt @@ -0,0 +1,22 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_TESTBED testbed ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_GROUPWISE groupwise ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_MSN msn ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_IRC irc ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_OSCAR oscar ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_YAHOO yahoo ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_WINPOPUP winpopup ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_SMS sms ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_JABBER jabber ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_GADU gadu ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_MEANWHILE meanwhile ) diff --git a/kopete/protocols/gadu/CMakeLists.txt b/kopete/protocols/gadu/CMakeLists.txt new file mode 100644 index 00000000..bb5326d4 --- /dev/null +++ b/kopete/protocols/gadu/CMakeLists.txt @@ -0,0 +1,50 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +add_subdirectory( ui ) +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${GADU_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_gadu.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_gadu (module) ###################### + +tde_add_kpart( kopete_gadu AUTOMOC + SOURCES + gaduaway.cpp gadueditcontact.cpp gaducommands.cpp + gadueditaccount.cpp gadusession.cpp gaducontact.cpp + gaduaddcontactpage.cpp gaduprotocol.cpp gaduaccount.cpp + gadupubdir.cpp gaduregisteraccount.cpp gaducontactlist.cpp + gadurichtextformat.cpp gadudccserver.cpp gadudcctransaction.cpp + gadudcc.cpp + LINK gaduui-static kopete-shared ${GADU_LIBRARIES} + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/gadu/ConfigureChecks.cmake b/kopete/protocols/gadu/ConfigureChecks.cmake new file mode 100644 index 00000000..1511e879 --- /dev/null +++ b/kopete/protocols/gadu/ConfigureChecks.cmake @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +pkg_search_module( GADU libgadu ) +if( NOT GADU_FOUND ) + tde_message_fatal( "libgadu is required, but was not found on your system" ) +endif( ) diff --git a/kopete/protocols/gadu/gadueditcontact.h b/kopete/protocols/gadu/gadueditcontact.h index e9413a2b..f135d55a 100644 --- a/kopete/protocols/gadu/gadueditcontact.h +++ b/kopete/protocols/gadu/gadueditcontact.h @@ -31,7 +31,6 @@ class TQLabel; class TQString; class TQWidget; class GaduContact; -class GaduContactsList::ContactLine; class TQListViewItem; class GaduEditContact : public KDialogBase diff --git a/kopete/protocols/gadu/icons/CMakeLists.txt b/kopete/protocols/gadu/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/gadu/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/gadu/libgadu/COPYING b/kopete/protocols/gadu/libgadu/COPYING deleted file mode 100644 index 071eee49..00000000 --- a/kopete/protocols/gadu/libgadu/COPYING +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/kopete/protocols/gadu/libgadu/Makefile.am b/kopete/protocols/gadu/libgadu/Makefile.am deleted file mode 100644 index 94693d38..00000000 --- a/kopete/protocols/gadu/libgadu/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -METASOURCES = AUTO -noinst_LTLIBRARIES = libgadu_copy.la -INCLUDES = $(SSL_INCLUDES) $(all_includes) -libgadu_copy_la_LDFLAGS = $(SSL_LDFLAGS) $(all_libraries) -libgadu_copy_la_LIBADD = $(LIBSSL) $(LIBPTHREAD) -libgadu_copy_la_SOURCES = common.c \ - dcc.c \ - events.c \ - http.c \ - libgadu.c \ - pubdir50.c \ - pubdir.c diff --git a/kopete/protocols/gadu/libgadu/common.c b/kopete/protocols/gadu/libgadu/common.c deleted file mode 100644 index 9e20422a..00000000 --- a/kopete/protocols/gadu/libgadu/common.c +++ /dev/null @@ -1,827 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo�ny <speedy@ziew.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif - -#include <errno.h> -#include <fcntl.h> -#include <netdb.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "libgadu.h" - -FILE *gg_debug_file = NULL; - -#ifndef GG_DEBUG_DISABLE - -/* - * gg_debug() // funkcja wewn�trzna - * - * wy�wietla komunikat o danym poziomie, o ile u�ytkownik sobie tego �yczy. - * - * - level - poziom wiadomo�ci - * - format... - tre�� wiadomo�ci (kompatybilna z printf()) - */ -void gg_debug(int level, const char *format, ...) -{ - va_list ap; - int old_errno = errno; - - if (gg_debug_handler) { - va_start(ap, format); - (*gg_debug_handler)(level, format, ap); - va_end(ap); - - goto cleanup; - } - - if ((gg_debug_level & level)) { - va_start(ap, format); - vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); - va_end(ap); - } - -cleanup: - errno = old_errno; -} - -#endif - -/* - * gg_vsaprintf() // funkcja pomocnicza - * - * robi dok�adnie to samo, co vsprintf(), tyle �e alokuje sobie wcze�niej - * miejsce na dane. powinno dzia�a� na tych maszynach, kt�re maj� funkcj� - * vsnprintf() zgodn� z C99, jak i na wcze�niejszych. - * - * - format - opis wy�wietlanego tekstu jak dla printf() - * - ap - lista argument�w dla printf() - * - * zaalokowany bufor, kt�ry nale�y p��niej zwolni�, lub NULL - * je�li nie uda�o si� wykona� zadania. - */ -char *gg_vsaprintf(const char *format, va_list ap) -{ - int size = 0; - const char *start; - char *buf = NULL; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY - va_list aq; - - va_copy(aq, ap); -#else -# ifdef __GG_LIBGADU_HAVE___VA_COPY - va_list aq; - - __va_copy(aq, ap); -# endif -#endif - - start = format; - -#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF - { - int res; - char *tmp; - - size = 128; - do { - size *= 2; - if (!(tmp = realloc(buf, size))) { - free(buf); - return NULL; - } - buf = tmp; - res = vsnprintf(buf, size, format, ap); - } while (res == size - 1 || res == -1); - } -#else - { - char tmp[2]; - - /* libce Solarisa przy buforze NULL zawsze zwracaj� -1, wi�c - * musimy poda� co� istniej�cego jako cel printf()owania. */ - size = vsnprintf(tmp, sizeof(tmp), format, ap); - if (!(buf = malloc(size + 1))) - return NULL; - } -#endif - - format = start; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY - vsnprintf(buf, size + 1, format, aq); - va_end(aq); -#else -# ifdef __GG_LIBGADU_HAVE___VA_COPY - vsnprintf(buf, size + 1, format, aq); - va_end(aq); -# else - vsnprintf(buf, size + 1, format, ap); -# endif -#endif - - return buf; -} - -/* - * gg_saprintf() // funkcja pomocnicza - * - * robi dok�adnie to samo, co sprintf(), tyle �e alokuje sobie wcze�niej - * miejsce na dane. powinno dzia�a� na tych maszynach, kt�re maj� funkcj� - * vsnprintf() zgodn� z C99, jak i na wcze�niejszych. - * - * - format... - tre�� taka sama jak w funkcji printf() - * - * zaalokowany bufor, kt�ry nale�y p��niej zwolni�, lub NULL - * je�li nie uda�o si� wykona� zadania. - */ -char *gg_saprintf(const char *format, ...) -{ - va_list ap; - char *res; - - va_start(ap, format); - res = gg_vsaprintf(format, ap); - va_end(ap); - - return res; -} - -/* - * gg_get_line() // funkcja pomocnicza - * - * podaje kolejn� lini� z bufora tekstowego. niszczy go bezpowrotnie, dziel�c - * na kolejne stringi. zdarza si�, nie ma potrzeby pisania funkcji dubluj�cej - * bufor �eby tylko mie� nieruszone dane wej�ciowe, skoro i tak nie b�d� nam - * po�niej potrzebne. obcina `\r\n'. - * - * - ptr - wska�nik do zmiennej, kt�ra przechowuje aktualn� pozycj� - * w przemiatanym buforze - * - * wska�nik do kolejnej linii tekstu lub NULL, je�li to ju� koniec bufora. - */ -char *gg_get_line(char **ptr) -{ - const char *foo; - char *res; - - if (!ptr || !*ptr || !strcmp(*ptr, "")) - return NULL; - - res = *ptr; - - if (!(foo = strchr(*ptr, '\n'))) - *ptr += strlen(*ptr); - else { - *ptr = foo + 1; - if (strlen(res) > 1 && res[strlen(res) - 1] == '\r') - res[strlen(res) - 1] = 0; - } - - return res; -} - -/* - * gg_connect() // funkcja pomocnicza - * - * ��czy si� z serwerem. pierwszy argument jest typu (void *), �eby nie - * musie� niczego inkludowa� w libgadu.h i nie psu� jaki� g�upich zale�no�ci - * na dziwnych systemach. - * - * - addr - adres serwera (struct in_addr *) - * - port - port serwera - * - async - asynchroniczne po��czenie - * - * deskryptor gniazda lub -1 w przypadku b��du (kod b��du w zmiennej errno). - */ -int gg_connect(void *addr, int port, int async) -{ - int sock, one = 1, errno2; - struct sockaddr_in sin; - struct in_addr *a = addr; - struct sockaddr_in myaddr; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async); - - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - memset(&myaddr, 0, sizeof(myaddr)); - myaddr.sin_family = AF_INET; - - myaddr.sin_addr.s_addr = gg_local_ip; - - if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - -#ifdef ASSIGN_SOCKETS_TO_THREADS - gg_win32_thread_socket(0, sock); -#endif - - if (async) { -#ifdef FIONBIO - if (ioctl(sock, FIONBIO, &one) == -1) { -#else - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return -1; - } - } - - sin.sin_port = htons(port); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = a->s_addr; - - if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) { - if (errno && (!async || errno != EINPROGRESS)) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return -1; - } - gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n"); - } - - return sock; -} - -/* - * gg_read_line() // funkcja pomocnicza - * - * czyta jedn� lini� tekstu z gniazda. - * - * - sock - deskryptor gniazda - * - buf - wska�nik do bufora - * - length - d�ugo�� bufora - * - * je�li trafi na b��d odczytu lub podano nieprawid�owe parametry, zwraca NULL. - * inaczej zwraca buf. - */ -char *gg_read_line(int sock, char *buf, int length) -{ - int ret; - - if (!buf || length < 0) - return NULL; - - for (; length > 1; buf++, length--) { - do { - if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) { - gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno)); - *buf = 0; - return NULL; - } else if (ret == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n"); - *buf = 0; - return NULL; - } - } while (ret == -1 && errno == EINTR); - - if (*buf == '\n') { - buf++; - break; - } - } - - *buf = 0; - return buf; -} - -/* - * gg_chomp() // funkcja pomocnicza - * - * ucina "\r\n" lub "\n" z ko�ca linii. - * - * - line - linia do przyci�cia - */ -void gg_chomp(char *line) -{ - int len; - - if (!line) - return; - - len = strlen(line); - - if (len > 0 && line[len - 1] == '\n') - line[--len] = 0; - if (len > 0 && line[len - 1] == '\r') - line[--len] = 0; -} - -/* - * gg_urlencode() // funkcja wewn�trzna - * - * zamienia podany tekst na ci�g znak�w do formularza http. przydaje si� - * przy r��nych us�ugach katalogu publicznego. - * - * - str - ci�g znak�w do zakodowania - * - * zaalokowany bufor, kt�ry nale�y p��niej zwolni� albo NULL - * w przypadku b��du. - */ -char *gg_urlencode(const char *str) -{ - char *q, *buf, hex[] = "0123456789abcdef"; - const char *p; - unsigned int size = 0; - - if (!str) - str = ""; - - for (p = str; *p; p++, size++) { - if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-')) - size += 2; - } - - if (!(buf = malloc(size + 1))) - return NULL; - - for (p = str, q = buf; *p; p++, q++) { - if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-')) - *q = *p; - else { - if (*p == ' ') - *q = '+'; - else { - *q++ = '%'; - *q++ = hex[*p >> 4 & 15]; - *q = hex[*p & 15]; - } - } - } - - *q = 0; - - return buf; -} - -/* - * gg_http_hash() // funkcja wewn�trzna - * - * funkcja licz�ca hash dla adresu e-mail, has�a i paru innych. - * - * - format... - format kolejnych parametr�w ('s' je�li dany parametr jest - * ci�giem znak�w lub 'u' je�li numerem GG) - * - * hash wykorzystywany przy rejestracji i wszelkich manipulacjach w�asnego - * wpisu w katalogu publicznym. - */ -int gg_http_hash(const char *format, ...) -{ - unsigned int a, c, i, j; - va_list ap; - int b = -1; - - va_start(ap, format); - - for (j = 0; j < strlen(format); j++) { - char *arg, buf[16]; - - if (format[j] == 'u') { - snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t)); - arg = buf; - } else { - if (!(arg = va_arg(ap, char*))) - arg = ""; - } - - i = 0; - while ((c = (unsigned char) arg[i++]) != 0) { - a = (c ^ b) + (c << 8); - b = (a >> 24) | (a << 8); - } - } - - va_end(ap); - - return (b < 0 ? -b : b); -} - -/* - * gg_gethostbyname() // funkcja pomocnicza - * - * odpowiednik gethostbyname() troszcz�cy si� o wsp��bie�no��, gdy mamy do - * dyspozycji funkcj� gethostbyname_r(). - * - * - hostname - nazwa serwera - * - * zwraca wska�nik na struktur� in_addr, kt�r� nale�y zwolni�. - */ -struct in_addr *gg_gethostbyname(const char *hostname) -{ - struct in_addr *addr = NULL; - -#ifdef HAVE_GETHOSTBYNAME_R - char *tmpbuf = NULL, *buf = NULL; - struct hostent *hp = NULL, *hp2 = NULL; - int h_errnop, ret; - size_t buflen = 1024; - int new_errno; - - new_errno = ENOMEM; - - if (!(addr = malloc(sizeof(struct in_addr)))) - goto cleanup; - - if (!(hp = calloc(1, sizeof(*hp)))) - goto cleanup; - - if (!(buf = malloc(buflen))) - goto cleanup; - - tmpbuf = buf; - - while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) { - buflen *= 2; - - if (!(tmpbuf = realloc(buf, buflen))) - break; - - buf = tmpbuf; - } - - if (ret) - new_errno = h_errnop; - - if (ret || !hp2 || !tmpbuf) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - free(buf); - free(hp); - - return addr; - -cleanup: - errno = new_errno; - - if (addr) - free(addr); - if (hp) - free(hp); - if (buf) - free(buf); - - return NULL; -#else - struct hostent *hp; - - if (!(addr = malloc(sizeof(struct in_addr)))) { - goto cleanup; - } - - if (!(hp = gethostbyname(hostname))) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - return addr; - -cleanup: - if (addr) - free(addr); - - return NULL; -#endif -} - -#ifdef ASSIGN_SOCKETS_TO_THREADS - -typedef struct gg_win32_thread { - int id; - int socket; - struct gg_win32_thread *next; -} gg_win32_thread; - -struct gg_win32_thread *gg_win32_threads = 0; - -/* - * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32 - * - * zwraca deskryptor gniazda, kt�re by�o ostatnio tworzone dla w�tku - * o podanym identyfikatorze. - * - * je�li na win32 przy po��czeniach synchronicznych zapami�tamy w jakim - * w�tku uruchomili�my funkcj�, kt�ra si� z czymkolwiek ��czy, to z osobnego - * w�tku mo�emy anulowa� po��czenie poprzez gg_win32_thread_socket(watek, -1); - * - * - thread_id - id w�tku. je�li jest r�wne 0, brany jest aktualny w�tek, - * je�li r�wne -1, usuwa wpis o podanym sockecie. - * - socket - deskryptor gniazda. je�li r�wne 0, zwraca deskryptor gniazda - * dla podanego w�tku, je�li r�wne -1, usuwa wpis, je�li co� - * innego, ustawia dla podanego w�tku dany numer deskryptora. - * - * je�li socket jest r�wne 0, zwraca deskryptor gniazda dla podanego w�tku. - */ -int gg_win32_thread_socket(int thread_id, int socket) -{ - char close = (thread_id == -1) || socket == -1; - gg_win32_thread *wsk = gg_win32_threads; - gg_win32_thread **p_wsk = &gg_win32_threads; - - if (!thread_id) - thread_id = GetCurrentThreadId(); - - while (wsk) { - if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) { - if (close) { - /* socket zostaje usuniety */ - closesocket(wsk->socket); - *p_wsk = wsk->next; - free(wsk); - return 1; - } else if (!socket) { - /* socket zostaje zwrocony */ - return wsk->socket; - } else { - /* socket zostaje ustawiony */ - wsk->socket = socket; - return socket; - } - } - p_wsk = &(wsk->next); - wsk = wsk->next; - } - - if (close && socket != -1) - closesocket(socket); - if (close || !socket) - return 0; - - /* Dodaje nowy element */ - wsk = malloc(sizeof(gg_win32_thread)); - wsk->id = thread_id; - wsk->socket = socket; - wsk->next = 0; - *p_wsk = wsk; - - return socket; -} - -#endif /* ASSIGN_SOCKETS_TO_THREADS */ - -static char gg_base64_charset[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -/* - * gg_base64_encode() - * - * zapisuje ci�g znak�w w base64. - * - * - buf - ci�g znak�w. - * - * zaalokowany bufor. - */ -char *gg_base64_encode(const char *buf) -{ - char *out, *res; - unsigned int i = 0, j = 0, k = 0, len = strlen(buf); - - res = out = malloc((len / 3 + 1) * 4 + 2); - - if (!res) - return NULL; - - while (j <= len) { - switch (i % 4) { - case 0: - k = (buf[j] & 252) >> 2; - break; - case 1: - if (j < len) - k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4); - else - k = (buf[j] & 3) << 4; - - j++; - break; - case 2: - if (j < len) - k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6); - else - k = (buf[j] & 15) << 2; - - j++; - break; - case 3: - k = buf[j++] & 63; - break; - } - *out++ = gg_base64_charset[k]; - i++; - } - - if (i % 4) - for (j = 0; j < 4 - (i % 4); j++, out++) - *out = '='; - - *out = 0; - - return res; -} - -/* - * gg_base64_decode() - * - * dekoduje ci�g znak�w z base64. - * - * - buf - ci�g znak�w. - * - * zaalokowany bufor. - */ -char *gg_base64_decode(const char *buf) -{ - const char *foo2; - char *res, *save, *foo, val; - const char *end; - unsigned int index = 0; - - if (!buf) - return NULL; - - save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2); - - if (!save) - return NULL; - - end = buf + strlen(buf); - - while (*buf && buf < end) { - if (*buf == '\r' || *buf == '\n') { - buf++; - continue; - } - if (!(foo2 = strchr(gg_base64_charset, *buf))) { - foo = gg_base64_charset; - } - else { - foo = foo2; - } - val = (int)(foo - gg_base64_charset); - buf++; - switch (index) { - case 0: - *res |= val << 2; - break; - case 1: - *res++ |= val >> 4; - *res |= val << 4; - break; - case 2: - *res++ |= val >> 2; - *res |= val << 6; - break; - case 3: - *res++ |= val; - break; - } - index++; - index %= 4; - } - *res = 0; - - return save; -} - -/* - * gg_proxy_auth() // funkcja wewn�trzna - * - * tworzy nag��wek autoryzacji dla proxy. - * - * zaalokowany tekst lub NULL, je�li proxy nie jest w��czone lub nie wymaga - * autoryzacji. - */ -char *gg_proxy_auth() -{ - char *tmp, *enc, *out; - unsigned int tmp_size; - - if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password) - return NULL; - - if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2)))) - return NULL; - - snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password); - - if (!(enc = gg_base64_encode(tmp))) { - free(tmp); - return NULL; - } - - free(tmp); - - if (!(out = malloc(strlen(enc) + 40))) { - free(enc); - return NULL; - } - - snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc); - - free(enc); - - return out; -} - -static uint32_t gg_crc32_table[256]; -static int gg_crc32_initialized = 0; - -/* - * gg_crc32_make_table() // funkcja wewn�trzna - */ -static void gg_crc32_make_table() -{ - uint32_t h = 1; - unsigned int i, j; - - memset(gg_crc32_table, 0, sizeof(gg_crc32_table)); - - for (i = 128; i; i >>= 1) { - h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0); - - for (j = 0; j < 256; j += 2 * i) - gg_crc32_table[i + j] = gg_crc32_table[j] ^ h; - } - - gg_crc32_initialized = 1; -} - -/* - * gg_crc32() - * - * wyznacza sum� kontroln� CRC32 danego bloku danych. - * - * - crc - suma kontrola poprzedniego bloku danych lub 0 je�li pierwszy - * - buf - bufor danych - * - size - ilo�� danych - * - * suma kontrolna CRC32. - */ -uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len) -{ - if (!gg_crc32_initialized) - gg_crc32_make_table(); - - if (!buf || len < 0) - return crc; - - crc ^= 0xffffffffL; - - while (len--) - crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff]; - - return crc ^ 0xffffffffL; -} - - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/compat.h b/kopete/protocols/gadu/libgadu/compat.h deleted file mode 100644 index 8b9098fe..00000000 --- a/kopete/protocols/gadu/libgadu/compat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo¼ny <speedy@ziew.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#ifndef __COMPAT_H -#define __COMPAT_H - -#ifdef sun -# define INADDR_NONE ((in_addr_t) 0xffffffff) -#endif - -#endif diff --git a/kopete/protocols/gadu/libgadu/dcc.c b/kopete/protocols/gadu/libgadu/dcc.c deleted file mode 100644 index d6b3c7cc..00000000 --- a/kopete/protocols/gadu/libgadu/dcc.c +++ /dev/null @@ -1,1298 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> - * Tomasz Chiliñski <chilek@chilan.com> - * Adam Wysocki <gophi@ekg.chmurka.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#include "compat.h" -#include "libgadu.h" - -#ifndef GG_DEBUG_DISABLE -/* - * gg_dcc_debug_data() // funkcja wewnêtrzna - * - * wy¶wietla zrzut pakietu w hexie. - * - * - prefix - prefiks zrzutu pakietu - * - fd - deskryptor gniazda - * - buf - bufor z danymi - * - size - rozmiar danych - */ -static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) -{ - unsigned int i; - - gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); - - for (i = 0; i < size; i++) - gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]); - - gg_debug(GG_DEBUG_MISC, "\n"); -} -#else -#define gg_dcc_debug_data(a,b,c,d) do { } while (0) -#endif - -/* - * gg_dcc_request() - * - * wysy³a informacjê o tym, ¿e dany klient powinien siê z nami po³±czyæ. - * wykorzystywane, kiedy druga strona, której chcemy co¶ wys³aæ jest za - * maskarad±. - * - * - sess - struktura opisuj±ca sesjê GG - * - uin - numerek odbiorcy - * - * patrz gg_send_message_ctcp(). - */ -int gg_dcc_request(struct gg_session *sess, uin_t uin) -{ - return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, "\002", 1); -} - -/* - * gg_dcc_fill_filetime() // funkcja wewnêtrzna - * - * zamienia czas w postaci unixowej na windowsowy. - * - * - unix - czas w postaci unixowej - * - filetime - czas w postaci windowsowej - */ -static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft) -{ -#ifdef __GG_LIBGADU_HAVE_LONG_LONG - unsigned long long tmp; - - tmp = ut; - tmp += 11644473600LL; - tmp *= 10000000LL; - -#ifndef __GG_LIBGADU_BIGENDIAN - ft[0] = (uint32_t) tmp; - ft[1] = (uint32_t) (tmp >> 32); -#else - ft[0] = gg_fix32((uint32_t) (tmp >> 32)); - ft[1] = gg_fix32((uint32_t) tmp); -#endif - -#endif -} - -/* - * gg_dcc_fill_file_info() - * - * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku. - * - * - d - struktura opisuj±ca po³±czenie DCC - * - filename - nazwa pliku - * - * 0, -1. - */ -int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) -{ - return gg_dcc_fill_file_info2(d, filename, filename); -} - -/* - * gg_dcc_fill_file_info2() - * - * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku. - * - * - d - struktura opisuj±ca po³±czenie DCC - * - filename - nazwa pliku - * - local_filename - nazwa na lokalnym systemie plików - * - * 0, -1. - */ -int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) -{ - struct stat st; - const char *name, *ext, *p; - unsigned char *q; - int i, j; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); - - if (!d || d->type != GG_SESSION_DCC_SEND) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); - errno = EINVAL; - return -1; - } - - if (stat(local_filename, &st) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno)); - return -1; - } - - if ((st.st_mode & S_IFDIR)) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); - errno = EINVAL; - return -1; - } - - if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); - return -1; - } - - memset(&d->file_info, 0, sizeof(d->file_info)); - - if (!(st.st_mode & S_IWUSR)) - d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); - - gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); - gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); - gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); - - d->file_info.size = gg_fix32(st.st_size); - d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ - - if (!(name = strrchr(filename, '/'))) - name = filename; - else - name++; - - if (!(ext = strrchr(name, '.'))) - ext = name + strlen(name); - - for (i = 0, p = name; i < 8 && p < ext; i++, p++) - d->file_info.short_filename[i] = toupper(name[i]); - - if (i == 8 && p < ext) { - d->file_info.short_filename[6] = '~'; - d->file_info.short_filename[7] = '1'; - } - - if (strlen(ext) > 0) { - for (j = 0; *ext && j < 4; j++, p++) - d->file_info.short_filename[i + j] = toupper(ext[j]); - } - - for (q = d->file_info.short_filename; *q; q++) { - if (*q == 185) { - *q = 165; - } else if (*q == 230) { - *q = 198; - } else if (*q == 234) { - *q = 202; - } else if (*q == 179) { - *q = 163; - } else if (*q == 241) { - *q = 209; - } else if (*q == 243) { - *q = 211; - } else if (*q == 156) { - *q = 140; - } else if (*q == 159) { - *q = 143; - } else if (*q == 191) { - *q = 175; - } - } - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); - strncpy(d->file_info.filename, name, sizeof(d->file_info.filename) - 1); - - return 0; -} - -/* - * gg_dcc_transfer() // funkcja wewnêtrzna - * - * inicjuje proces wymiany pliku z danym klientem. - * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - w³asny numer - * - peer_uin - numer obiorcy - * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET) - * - * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. - */ -static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) -{ - struct gg_dcc *d = NULL; - struct in_addr addr; - - addr.s_addr = ip; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); - - if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); - errno = EINVAL; - return NULL; - } - - if (!(d = (void*) calloc(1, sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n"); - return NULL; - } - - d->check = GG_CHECK_WRITE; - d->state = GG_STATE_CONNECTING; - d->type = type; - d->timeout = GG_DEFAULT_TIMEOUT; - d->file_fd = -1; - d->active = 1; - d->fd = -1; - d->uin = my_uin; - d->peer_uin = peer_uin; - - if ((d->fd = gg_connect(&addr, port, 1)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n"); - free(d); - return NULL; - } - - return d; -} - -/* - * gg_dcc_get_file() - * - * inicjuje proces odbierania pliku od danego klienta, gdy ten wys³a³ do - * nas ¿±danie po³±czenia. - * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - w³asny numer - * - peer_uin - numer obiorcy - * - * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. - */ -struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) -{ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n"); - - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); -} - -/* - * gg_dcc_send_file() - * - * inicjuje proces wysy³ania pliku do danego klienta. - * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - w³asny numer - * - peer_uin - numer obiorcy - * - * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. - */ -struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) -{ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n"); - - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); -} - -/* - * gg_dcc_voice_chat() - * - * próbuje nawi±zaæ po³±czenie g³osowe. - * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - w³asny numer - * - peer_uin - numer obiorcy - * - * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. - */ -struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) -{ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n"); - - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); -} - -/* - * gg_dcc_set_type() - * - * po zdarzeniu GG_EVENT_DCC_CALLBACK nale¿y ustawiæ typ po³±czenia za - * pomoc± tej funkcji. - * - * - d - struktura opisuj±ca po³±czenie - * - type - typ po³±czenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE) - */ -void gg_dcc_set_type(struct gg_dcc *d, int type) -{ - d->type = type; - d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST; -} - -/* - * gg_dcc_callback() // funkcja wewnêtrzna - * - * wywo³ywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza - * rezultat w struct gg_dcc->event. - * - * - d - structura opisuj±ca po³±czenie - * - * 0, -1. - */ -static int gg_dcc_callback(struct gg_dcc *d) -{ - struct gg_event *e = gg_dcc_watch_fd(d); - - d->event = e; - - return (e != NULL) ? 0 : -1; -} - -/* - * gg_dcc_socket_create() - * - * tworzy gniazdo dla bezpo¶redniej komunikacji miêdzy klientami. - * - * - uin - w³asny numer - * - port - preferowany port, je¶li równy 0 lub -1, próbuje domy¶lnego - * - * zaalokowana struct gg_dcc, któr± po¼niej nale¿y zwolniæ funkcj± - * gg_dcc_free(), albo NULL je¶li wyst±pi³ b³±d. - */ -struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) -{ - struct gg_dcc *c; - struct sockaddr_in sin; - int sock, bound = 0, errno2; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); - - if (!uin) { - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); - errno = EINVAL; - return NULL; - } - - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno)); - return NULL; - } - - if (!port) - port = GG_DEFAULT_DCC_PORT; - - while (!bound) { - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons(port); - - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); - if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) - bound = 1; - else { - if (++port == 65535) { - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n"); - close(sock); - return NULL; - } - } - } - - if (listen(sock, 10)) { - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); - - if (!(c = malloc(sizeof(*c)))) { - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n"); - close(sock); - return NULL; - } - memset(c, 0, sizeof(*c)); - - c->port = c->id = port; - c->fd = sock; - c->type = GG_SESSION_DCC_SOCKET; - c->uin = uin; - c->timeout = -1; - c->state = GG_STATE_LISTENING; - c->check = GG_CHECK_READ; - c->callback = gg_dcc_callback; - c->destroy = gg_dcc_free; - - return c; -} - -/* - * gg_dcc_voice_send() - * - * wysy³a ramkê danych dla rozmowy g³osowej. - * - * - d - struktura opisuj±ca po³±czenie dcc - * - buf - bufor z danymi - * - length - rozmiar ramki - * - * 0, -1. - */ -int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) -{ - struct packet_s { - uint8_t type; - uint32_t length; - } GG_PACKED; - struct packet_s packet; - - gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length); - if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n"); - errno = EINVAL; - return -1; - } - - packet.type = 0x03; /* XXX */ - packet.length = gg_fix32(length); - - if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); - return -1; - } - gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet)); - - if (write(d->fd, buf, length) < length) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); - return -1; - } - gg_dcc_debug_data("write", d->fd, buf, length); - - return 0; -} - -#define gg_read(fd, buf, size) \ -{ \ - int tmp = read(fd, buf, size); \ - \ - if (tmp < (int) size) { \ - if (tmp == -1) { \ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \ - } else if (tmp == 0) { \ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \ - } else { \ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \ - } \ - e->type = GG_EVENT_DCC_ERROR; \ - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ - return e; \ - } \ - gg_dcc_debug_data("read", fd, buf, size); \ -} - -#define gg_write(fd, buf, size) \ -{ \ - int tmp; \ - gg_dcc_debug_data("write", fd, buf, size); \ - tmp = write(fd, buf, size); \ - if (tmp < (int) size) { \ - if (tmp == -1) { \ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \ - } else { \ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \ - } \ - e->type = GG_EVENT_DCC_ERROR; \ - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ - return e; \ - } \ -} - -/* - * gg_dcc_watch_fd() - * - * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê zmieni na gg_dcc->fd. - * - * - h - struktura zwrócona przez gg_create_dcc_socket() - * - * zaalokowana struct gg_event lub NULL, je¶li zabrak³o pamiêci na ni±. - */ -struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) -{ - struct gg_event *e; - int foo; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); - - if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); - errno = EINVAL; - return NULL; - } - - if (!(e = (void*) calloc(1, sizeof(*e)))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); - return NULL; - } - - e->type = GG_EVENT_NONE; - - if (h->type == GG_SESSION_DCC_SOCKET) { - struct sockaddr_in sin; - struct gg_dcc *c; - int fd, sin_len = sizeof(sin), one = 1; - - if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); - return e; - } - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); - -#ifdef FIONBIO - if (ioctl(fd, FIONBIO, &one) == -1) { -#else - if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno)); - close(fd); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - return e; - } - - if (!(c = (void*) calloc(1, sizeof(*c)))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); - - free(e); - close(fd); - return NULL; - } - - c->fd = fd; - c->check = GG_CHECK_READ; - c->state = GG_STATE_READING_UIN_1; - c->type = GG_SESSION_DCC; - c->timeout = GG_DEFAULT_TIMEOUT; - c->file_fd = -1; - c->remote_addr = sin.sin_addr.s_addr; - c->remote_port = ntohs(sin.sin_port); - - e->type = GG_EVENT_DCC_NEW; - e->event.dcc_new = c; - - return e; - } else { - struct gg_dcc_tiny_packet tiny; - struct gg_dcc_small_packet small; - struct gg_dcc_big_packet big; - int size, tmp, res, res_size = sizeof(res); - unsigned int utmp; - char buf[1024], ack[] = "UDAG"; - - struct gg_dcc_file_info_packet { - struct gg_dcc_big_packet big; - struct gg_file_info file_info; - } GG_PACKED; - struct gg_dcc_file_info_packet file_info_packet; - - switch (h->state) { - case GG_STATE_READING_UIN_1: - case GG_STATE_READING_UIN_2: - { - uin_t uin; - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); - - gg_read(h->fd, &uin, sizeof(uin)); - - if (h->state == GG_STATE_READING_UIN_1) { - h->state = GG_STATE_READING_UIN_2; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - h->peer_uin = gg_fix32(uin); - } else { - h->state = GG_STATE_SENDING_ACK; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - h->uin = gg_fix32(uin); - e->type = GG_EVENT_DCC_CLIENT_ACCEPT; - } - - return e; - } - - case GG_STATE_SENDING_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); - - gg_write(h->fd, ack, 4); - - h->state = GG_STATE_READING_TYPE; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - - case GG_STATE_READING_TYPE: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); - - gg_read(h->fd, &small, sizeof(small)); - - small.type = gg_fix32(small.type); - - switch (small.type) { - case 0x0003: /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); - h->type = GG_SESSION_DCC_SEND; - h->state = GG_STATE_SENDING_FILE_INFO; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - - e->type = GG_EVENT_DCC_CALLBACK; - - break; - - case 0x0002: /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); - h->type = GG_SESSION_DCC_GET; - h->state = GG_STATE_READING_REQUEST; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - h->incoming = 1; - - break; - - default: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - } - - return e; - - case GG_STATE_READING_REQUEST: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); - - gg_read(h->fd, &small, sizeof(small)); - - small.type = gg_fix32(small.type); - - switch (small.type) { - case 0x0001: /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); - h->state = GG_STATE_READING_FILE_INFO; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - break; - - case 0x0003: /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); - h->state = GG_STATE_SENDING_VOICE_ACK; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; - h->type = GG_SESSION_DCC_VOICE; - e->type = GG_EVENT_DCC_NEED_VOICE_ACK; - - break; - - default: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - } - - return e; - - case GG_STATE_READING_FILE_INFO: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); - - gg_read(h->fd, &file_info_packet, sizeof(file_info_packet)); - - memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); - - h->file_info.mode = gg_fix32(h->file_info.mode); - h->file_info.size = gg_fix32(h->file_info.size); - - h->state = GG_STATE_SENDING_FILE_ACK; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DCC_TIMEOUT_FILE_ACK; - - e->type = GG_EVENT_DCC_NEED_FILE_ACK; - - return e; - - case GG_STATE_SENDING_FILE_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); - - big.type = gg_fix32(0x0006); /* XXX */ - big.dunno1 = gg_fix32(h->offset); - big.dunno2 = 0; - - gg_write(h->fd, &big, sizeof(big)); - - h->state = GG_STATE_READING_FILE_HEADER; - h->chunk_size = sizeof(big); - h->chunk_offset = 0; - if (!(h->chunk_buf = malloc(sizeof(big)))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); - free(e); - return NULL; - } - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - - case GG_STATE_SENDING_VOICE_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); - - tiny.type = 0x01; /* XXX */ - - gg_write(h->fd, &tiny, sizeof(tiny)); - - h->state = GG_STATE_READING_VOICE_HEADER; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - h->offset = 0; - - return e; - - case GG_STATE_READING_FILE_HEADER: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); - - tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); - - if (tmp == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - return e; - } - - gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); - - h->chunk_offset += tmp; - - if (h->chunk_offset < h->chunk_size) - return e; - - memcpy(&big, h->chunk_buf, sizeof(big)); - free(h->chunk_buf); - h->chunk_buf = NULL; - - big.type = gg_fix32(big.type); - h->chunk_size = gg_fix32(big.dunno1); - h->chunk_offset = 0; - - if (big.type == 0x0005) { /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_REFUSED; - return e; - } - - if (h->chunk_size == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); - e->type = GG_EVENT_DCC_DONE; - return e; - } - - h->state = GG_STATE_GETTING_FILE; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - h->established = 1; - - return e; - - case GG_STATE_READING_VOICE_HEADER: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); - - gg_read(h->fd, &tiny, sizeof(tiny)); - - switch (tiny.type) { - case 0x03: /* XXX */ - h->state = GG_STATE_READING_VOICE_SIZE; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - h->established = 1; - break; - case 0x04: /* XXX */ - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); - /* XXX zwracaæ odpowiedni event */ - default: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - } - - return e; - - case GG_STATE_READING_VOICE_SIZE: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); - - gg_read(h->fd, &small, sizeof(small)); - - small.type = gg_fix32(small.type); - - if (small.type < 16 || small.type > sizeof(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - - return e; - } - - h->chunk_size = small.type; - h->chunk_offset = 0; - - if (!(h->voice_buf = malloc(h->chunk_size))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); - free(e); - return NULL; - } - - h->state = GG_STATE_READING_VOICE_DATA; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - - case GG_STATE_READING_VOICE_DATA: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); - - tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); - if (tmp < 1) { - if (tmp == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); - } else { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); - } - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - return e; - } - - gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); - - h->chunk_offset += tmp; - - if (h->chunk_offset >= h->chunk_size) { - e->type = GG_EVENT_DCC_VOICE_DATA; - e->event.dcc_voice_data.data = h->voice_buf; - e->event.dcc_voice_data.length = h->chunk_size; - h->state = GG_STATE_READING_VOICE_HEADER; - h->voice_buf = NULL; - } - - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - - case GG_STATE_CONNECTING: - { - uin_t uins[2]; - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); - - res = 0; - if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - return e; - } - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); - - uins[0] = gg_fix32(h->uin); - uins[1] = gg_fix32(h->peer_uin); - - gg_write(h->fd, uins, sizeof(uins)); - - h->state = GG_STATE_READING_ACK; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - } - - case GG_STATE_READING_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); - - gg_read(h->fd, buf, 4); - - if (strncmp(buf, ack, 4)) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); - - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - return e; - } - - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - h->state = GG_STATE_SENDING_REQUEST; - - return e; - - case GG_STATE_SENDING_VOICE_REQUEST: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); - - small.type = gg_fix32(0x0003); - - gg_write(h->fd, &small, sizeof(small)); - - h->state = GG_STATE_READING_VOICE_ACK; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - return e; - - case GG_STATE_SENDING_REQUEST: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); - - small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); - - switch (h->type) { - case GG_SESSION_DCC_GET: - h->state = GG_STATE_READING_REQUEST; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - break; - - case GG_SESSION_DCC_SEND: - h->state = GG_STATE_SENDING_FILE_INFO; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - - if (h->file_fd == -1) - e->type = GG_EVENT_DCC_NEED_FILE_INFO; - break; - - case GG_SESSION_DCC_VOICE: - h->state = GG_STATE_SENDING_VOICE_REQUEST; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - return e; - - case GG_STATE_SENDING_FILE_INFO: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); - - if (h->file_fd == -1) { - e->type = GG_EVENT_DCC_NEED_FILE_INFO; - return e; - } - - small.type = gg_fix32(0x0001); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); - - file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ - file_info_packet.big.dunno1 = 0; - file_info_packet.big.dunno2 = 0; - - memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); - - /* zostaj± teraz u nas, wiêc odwracamy z powrotem */ - h->file_info.size = gg_fix32(h->file_info.size); - h->file_info.mode = gg_fix32(h->file_info.mode); - - gg_write(h->fd, &file_info_packet, sizeof(file_info_packet)); - - h->state = GG_STATE_READING_FILE_ACK; - h->check = GG_CHECK_READ; - h->timeout = GG_DCC_TIMEOUT_FILE_ACK; - - return e; - - case GG_STATE_READING_FILE_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); - - gg_read(h->fd, &big, sizeof(big)); - - /* XXX sprawdzaæ wynik */ - h->offset = gg_fix32(big.dunno1); - - h->state = GG_STATE_SENDING_FILE_HEADER; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - - e->type = GG_EVENT_DCC_ACK; - - return e; - - case GG_STATE_READING_VOICE_ACK: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); - - gg_read(h->fd, &tiny, sizeof(tiny)); - - if (tiny.type != 0x01) { - gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_REFUSED; - return e; - } - - h->state = GG_STATE_READING_VOICE_HEADER; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - - e->type = GG_EVENT_DCC_ACK; - - return e; - - case GG_STATE_SENDING_FILE_HEADER: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); - - h->chunk_offset = 0; - - if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { - h->chunk_size = 4096; - big.type = gg_fix32(0x0003); /* XXX */ - } else - big.type = gg_fix32(0x0002); /* XXX */ - - big.dunno1 = gg_fix32(h->chunk_size); - big.dunno2 = 0; - - gg_write(h->fd, &big, sizeof(big)); - - h->state = GG_STATE_SENDING_FILE; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - h->established = 1; - - return e; - - case GG_STATE_SENDING_FILE: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); - - if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) - utmp = sizeof(buf); - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); - - /* koniec pliku? */ - if (h->file_info.size == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n"); - e->type = GG_EVENT_DCC_DONE; - - return e; - } - - lseek(h->file_fd, h->offset, SEEK_SET); - - size = read(h->file_fd, buf, utmp); - - /* b³±d */ - if (size == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); - - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_FILE; - - return e; - } - - /* koniec pliku? */ - if (size == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_EOF; - - return e; - } - - /* je¶li wczytali¶my wiêcej, utnijmy. */ - if (h->offset + size > h->file_info.size) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); - size = h->file_info.size - h->offset; - - if (size < 1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n"); - e->type = GG_EVENT_DCC_DONE; - return e; - } - } - - tmp = write(h->fd, buf, size); - - if (tmp == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno)); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - return e; - } - - h->offset += size; - - if (h->offset >= h->file_info.size) { - e->type = GG_EVENT_DCC_DONE; - return e; - } - - h->chunk_offset += size; - - if (h->chunk_offset >= h->chunk_size) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); - h->state = GG_STATE_SENDING_FILE_HEADER; - h->timeout = GG_DEFAULT_TIMEOUT; - } else { - h->state = GG_STATE_SENDING_FILE; - h->timeout = GG_DCC_TIMEOUT_SEND; - } - - h->check = GG_CHECK_WRITE; - - return e; - - case GG_STATE_GETTING_FILE: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); - - if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) - utmp = sizeof(buf); - - size = read(h->fd, buf, utmp); - - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size); - - /* b³±d */ - if (size == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); - - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - - return e; - } - - /* koniec? */ - if (size == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_EOF; - - return e; - } - - tmp = write(h->file_fd, buf, size); - - if (tmp == -1 || tmp < size) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_NET; - return e; - } - - h->offset += size; - - if (h->offset >= h->file_info.size) { - e->type = GG_EVENT_DCC_DONE; - return e; - } - - h->chunk_offset += size; - - if (h->chunk_offset >= h->chunk_size) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); - h->state = GG_STATE_READING_FILE_HEADER; - h->timeout = GG_DEFAULT_TIMEOUT; - h->chunk_offset = 0; - h->chunk_size = sizeof(big); - if (!(h->chunk_buf = malloc(sizeof(big)))) { - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); - free(e); - return NULL; - } - } else { - h->state = GG_STATE_GETTING_FILE; - h->timeout = GG_DCC_TIMEOUT_GET; - } - - h->check = GG_CHECK_READ; - - return e; - - default: - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); - e->type = GG_EVENT_DCC_ERROR; - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; - - return e; - } - } - - return e; -} - -#undef gg_read -#undef gg_write - -/* - * gg_dcc_free() - * - * zwalnia pamiêæ po strukturze po³±czenia dcc. - * - * - d - zwalniana struktura - */ -void gg_dcc_free(struct gg_dcc *d) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); - - if (!d) - return; - - if (d->fd != -1) - close(d->fd); - - if (d->chunk_buf) { - free(d->chunk_buf); - d->chunk_buf = NULL; - } - - free(d); -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/events.c b/kopete/protocols/gadu/libgadu/events.c deleted file mode 100644 index 1bf67690..00000000 --- a/kopete/protocols/gadu/libgadu/events.c +++ /dev/null @@ -1,1580 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo�ny <speedy@ziew.org> - * Arkadiusz Mi�kiewicz <arekm@pld-linux.org> - * Adam Wysocki <gophi@ekg.chmurka.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include "libgadu-config.h" - -#include <errno.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL -# include <openssl/err.h> -# include <openssl/x509.h> -#endif - -#include "compat.h" -#include "libgadu.h" - -/* - * gg_event_free() - * - * zwalnia pami�� zajmowan� przez informacj� o zdarzeniu. - * - * - e - wska�nik do informacji o zdarzeniu - */ -void gg_event_free(struct gg_event *e) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); - - if (!e) - return; - - switch (e->type) { - case GG_EVENT_MSG: - free(e->event.msg.message); - free(e->event.msg.formats); - free(e->event.msg.recipients); - break; - - case GG_EVENT_NOTIFY: - free(e->event.notify); - break; - - case GG_EVENT_NOTIFY60: - { - int i; - - for (i = 0; e->event.notify60[i].uin; i++) - free(e->event.notify60[i].descr); - - free(e->event.notify60); - - break; - } - - case GG_EVENT_STATUS60: - free(e->event.status60.descr); - break; - - case GG_EVENT_STATUS: - free(e->event.status.descr); - break; - - case GG_EVENT_NOTIFY_DESCR: - free(e->event.notify_descr.notify); - free(e->event.notify_descr.descr); - break; - - case GG_EVENT_DCC_VOICE_DATA: - free(e->event.dcc_voice_data.data); - break; - - case GG_EVENT_PUBDIR50_SEARCH_REPLY: - case GG_EVENT_PUBDIR50_READ: - case GG_EVENT_PUBDIR50_WRITE: - gg_pubdir50_free(e->event.pubdir50); - break; - - case GG_EVENT_USERLIST: - free(e->event.userlist.reply); - break; - - case GG_EVENT_IMAGE_REPLY: - free(e->event.image_reply.filename); - free(e->event.image_reply.image); - break; - } - - free(e); -} - -/* - * gg_image_queue_remove() - * - * usuwa z kolejki dany wpis. - * - * - s - sesja - * - q - kolejka - * - freeq - czy zwolni� kolejk� - * - * 0/-1 - */ -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) -{ - if (!s || !q) { - errno = EFAULT; - return -1; - } - - if (s->images == q) - s->images = q->next; - else { - struct gg_image_queue *qq; - - for (qq = s->images; qq; qq = qq->next) { - if (qq->next == q) { - qq->next = q->next; - break; - } - } - } - - if (freeq) { - free(q->image); - free(q->filename); - free(q); - } - - return 0; -} - -/* - * gg_image_queue_parse() // funkcja wewn�trzna - * - * parsuje przychodz�cy pakiet z obrazkiem. - * - * - e - opis zdarzenia - * - - */ -static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) -{ - struct gg_msg_image_reply *i = (void*) p; - struct gg_image_queue *q, *qq; - - if (!p || !sess || !e) { - errno = EFAULT; - return; - } - - /* znajd� dany obrazek w kolejce danej sesji */ - - for (qq = sess->images, q = NULL; qq; qq = qq->next) { - if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { - q = qq; - break; - } - } - - if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); - return; - } - - if (p[0] == 0x05) { - int i, ok = 0; - - q->done = 0; - - len -= sizeof(struct gg_msg_image_reply); - p += sizeof(struct gg_msg_image_reply); - - /* sprawd�, czy mamy tekst zako�czony \0 */ - - for (i = 0; i < len; i++) { - if (!p[i]) { - ok = 1; - break; - } - } - - if (!ok) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); - return; - } - - if (!(q->filename = strdup(p))) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); - return; - } - - len -= strlen(p) + 1; - p += strlen(p) + 1; - } else { - len -= sizeof(struct gg_msg_image_reply); - p += sizeof(struct gg_msg_image_reply); - } - - if (q->done + len > q->size) - len = q->size - q->done; - - memcpy(q->image + q->done, p, len); - q->done += len; - - /* je�li sko�czono odbiera� obrazek, wygeneruj zdarzenie */ - - if (q->done >= q->size) { - e->type = GG_EVENT_IMAGE_REPLY; - e->event.image_reply.sender = sender; - e->event.image_reply.size = q->size; - e->event.image_reply.crc32 = q->crc32; - e->event.image_reply.filename = q->filename; - e->event.image_reply.image = q->image; - - gg_image_queue_remove(sess, q, 0); - - free(q); - } -} - -/* - * gg_handle_recv_msg() // funkcja wewn�trzna - * - * obs�uguje pakiet z przychodz�c� wiadomo�ci�, rozbijaj�c go na dodatkowe - * struktury (konferencje, kolorki) w razie potrzeby. - * - * - h - nag��wek pakietu - * - e - opis zdarzenia - * - * 0, -1. - */ -static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) -{ - struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); - char *p, *packet_end = (char*) r + h->length; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); - - if (!r->seq && !r->msgclass) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); - e->type = GG_EVENT_NONE; - return 0; - } - - for (p = (char*) r + sizeof(*r); *p; p++) { - if (*p == 0x02 && p == packet_end - 1) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); - break; - } - if (p >= packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); - goto malformed; - } - } - - p++; - - /* przeanalizuj dodatkowe opcje */ - while (p < packet_end) { - switch (*p) { - case 0x01: /* konferencja */ - { - struct gg_msg_recipients *m = (void*) p; - uint32_t i, count; - - p += sizeof(*m); - - if (p > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); - goto malformed; - } - - count = gg_fix32(m->count); - - if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); - goto malformed; - } - - if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); - goto fail; - } - - for (i = 0; i < count; i++, p += sizeof(uint32_t)) { - uint32_t u; - memcpy(&u, p, sizeof(uint32_t)); - e->event.msg.recipients[i] = gg_fix32(u); - } - - e->event.msg.recipients_count = count; - - break; - } - - case 0x02: /* richtext */ - { - uint16_t len; - char *buf; - - if (p + 3 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); - goto malformed; - } - - memcpy(&len, p + 1, sizeof(uint16_t)); - len = gg_fix16(len); - - if (!(buf = malloc(len))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); - goto fail; - } - - p += 3; - - if (p + len > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); - free(buf); - goto malformed; - } - - memcpy(buf, p, len); - - e->event.msg.formats = buf; - e->event.msg.formats_length = len; - - p += len; - - break; - } - - case 0x04: /* image_request */ - { - struct gg_msg_image_request *i = (void*) p; - - if (p + sizeof(*i) > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); - goto malformed; - } - - e->event.image_request.sender = gg_fix32(r->sender); - e->event.image_request.size = gg_fix32(i->size); - e->event.image_request.crc32 = gg_fix32(i->crc32); - - e->type = GG_EVENT_IMAGE_REQUEST; - - return 0; - } - - case 0x05: /* image_reply */ - case 0x06: - { - struct gg_msg_image_reply *rep = (void*) p; - - if (p + sizeof(struct gg_msg_image_reply) == packet_end) { - - /* pusta odpowied� - klient po drugiej stronie nie ma ��danego obrazka */ - - e->type = GG_EVENT_IMAGE_REPLY; - e->event.image_reply.sender = gg_fix32(r->sender); - e->event.image_reply.size = 0; - e->event.image_reply.crc32 = gg_fix32(rep->crc32); - e->event.image_reply.filename = NULL; - e->event.image_reply.image = NULL; - return 0; - - } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { - - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); - goto malformed; - } - - rep->size = gg_fix32(rep->size); - rep->crc32 = gg_fix32(rep->crc32); - gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); - - return 0; - } - - default: - { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); - p = packet_end; - } - } - } - - e->type = GG_EVENT_MSG; - e->event.msg.msgclass = gg_fix32(r->msgclass); - e->event.msg.sender = gg_fix32(r->sender); - e->event.msg.time = gg_fix32(r->time); - e->event.msg.message = strdup((char*) r + sizeof(*r)); - - return 0; - -malformed: - e->type = GG_EVENT_NONE; - - free(e->event.msg.recipients); - free(e->event.msg.formats); - - return 0; - -fail: - free(e->event.msg.recipients); - free(e->event.msg.formats); - return -1; -} - -/* - * gg_watch_fd_connected() // funkcja wewn�trzna - * - * patrzy na gniazdo, odbiera pakiet i wype�nia struktur� zdarzenia. - * - * - sess - struktura opisuj�ca sesj� - * - e - opis zdarzenia - * - * 0, -1. - */ -static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) -{ - struct gg_header *h = NULL; - char *p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; - } - - p = (char*) h + sizeof(struct gg_header); - - switch (h->type) { - case GG_RECV_MSG: - { - if (h->length >= sizeof(struct gg_recv_msg)) - if (gg_handle_recv_msg(h, e, sess)) - goto fail; - - break; - } - - case GG_NOTIFY_REPLY: - { - struct gg_notify_reply *n = (void*) p; - unsigned int count, i; - char *tmp; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - - if (h->length < sizeof(*n)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); - errno = EINVAL; - goto fail; - } - - if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { - e->type = GG_EVENT_NOTIFY_DESCR; - - if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - goto fail; - } - e->event.notify_descr.notify[1].uin = 0; - memcpy(e->event.notify_descr.notify, p, sizeof(*n)); - e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); - e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); - e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); - - count = h->length - sizeof(*n); - if (!(tmp = malloc(count + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - goto fail; - } - memcpy(tmp, p + sizeof(*n), count); - tmp[count] = 0; - e->event.notify_descr.descr = tmp; - - } else { - e->type = GG_EVENT_NOTIFY; - - if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - goto fail; - } - - memcpy(e->event.notify, p, h->length); - count = h->length / sizeof(*n); - e->event.notify[count].uin = 0; - - for (i = 0; i < count; i++) { - e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); - e->event.notify[i].status = gg_fix32(e->event.notify[i].status); - e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); - } - } - - break; - } - - case GG_STATUS: - { - struct gg_status *s = (void*) p; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - - if (h->length >= sizeof(*s)) { - e->type = GG_EVENT_STATUS; - memcpy(&e->event.status, p, sizeof(*s)); - e->event.status.uin = gg_fix32(e->event.status.uin); - e->event.status.status = gg_fix32(e->event.status.status); - if (h->length > sizeof(*s)) { - int len = h->length - sizeof(*s); - char *buf = malloc(len + 1); - if (buf) { - memcpy(buf, p + sizeof(*s), len); - buf[len] = 0; - } - e->event.status.descr = buf; - } else - e->event.status.descr = NULL; - } - - break; - } - - case GG_NOTIFY_REPLY60: - { - struct gg_notify_reply60 *n = (void*) p; - unsigned int length = h->length, i = 0; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - - e->type = GG_EVENT_NOTIFY60; - e->event.notify60 = malloc(sizeof(*e->event.notify60)); - - if (!e->event.notify60) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - goto fail; - } - - e->event.notify60[0].uin = 0; - - while (length >= sizeof(struct gg_notify_reply60)) { - uin_t uin = gg_fix32(n->uin); - char *tmp; - - e->event.notify60[i].uin = uin & 0x00ffffff; - e->event.notify60[i].status = n->status; - e->event.notify60[i].remote_ip = n->remote_ip; - e->event.notify60[i].remote_port = gg_fix16(n->remote_port); - e->event.notify60[i].version = n->version; - e->event.notify60[i].image_size = n->image_size; - e->event.notify60[i].descr = NULL; - e->event.notify60[i].time = 0; - - if (uin & 0x40000000) - e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; - if (uin & 0x08000000) - e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; - - if (GG_S_D(n->status)) { - unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - - if (descr_len < length) { - if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - goto fail; - } - - memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); - e->event.notify60[i].descr[descr_len] = 0; - - /* XXX czas */ - } - - length -= sizeof(struct gg_notify_reply60) + descr_len + 1; - n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); - } else { - length -= sizeof(struct gg_notify_reply60); - n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); - } - - if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); - free(e->event.notify60); - goto fail; - } - - e->event.notify60 = (void*) tmp; - e->event.notify60[++i].uin = 0; - } - - break; - } - - case GG_STATUS60: - { - struct gg_status60 *s = (void*) p; - uint32_t uin; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - - if (h->length < sizeof(*s)) - break; - - uin = gg_fix32(s->uin); - - e->type = GG_EVENT_STATUS60; - e->event.status60.uin = uin & 0x00ffffff; - e->event.status60.status = s->status; - e->event.status60.remote_ip = s->remote_ip; - e->event.status60.remote_port = gg_fix16(s->remote_port); - e->event.status60.version = s->version; - e->event.status60.image_size = s->image_size; - e->event.status60.descr = NULL; - e->event.status60.time = 0; - - if (uin & 0x40000000) - e->event.status60.version |= GG_HAS_AUDIO_MASK; - if (uin & 0x08000000) - e->event.status60.version |= GG_ERA_OMNIX_MASK; - - if (h->length > sizeof(*s)) { - int len = h->length - sizeof(*s); - char *buf = malloc(len + 1); - - if (buf) { - memcpy(buf, (char*) p + sizeof(*s), len); - buf[len] = 0; - } - - e->event.status60.descr = buf; - - if (len > 4 && p[h->length - 5] == 0) { - uint32_t t; - memcpy(&t, p + h->length - 4, sizeof(uint32_t)); - e->event.status60.time = gg_fix32(t); - } - } - - break; - } - - case GG_SEND_MSG_ACK: - { - struct gg_send_msg_ack *s = (void*) p; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); - - if (h->length < sizeof(*s)) - break; - - e->type = GG_EVENT_ACK; - e->event.ack.status = gg_fix32(s->status); - e->event.ack.recipient = gg_fix32(s->recipient); - e->event.ack.seq = gg_fix32(s->seq); - - break; - } - - case GG_PONG: - { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); - - e->type = GG_EVENT_PONG; - sess->last_pong = time(NULL); - - break; - } - - case GG_DISCONNECTING: - { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); - e->type = GG_EVENT_DISCONNECT; - break; - } - - case GG_PUBDIR50_REPLY: - { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); - if (gg_pubdir50_handle_reply(e, p, h->length) == -1) - goto fail; - break; - } - - case GG_USERLIST_REPLY: - { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); - - if (h->length < 1) - break; - - /* je�li odpowied� na eksport, wywo�aj zdarzenie tylko - * gdy otrzymano wszystkie odpowiedzi */ - if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { - if (--sess->userlist_blocks) - break; - - p[0] = GG_USERLIST_PUT_REPLY; - } - - if (h->length > 1) { - char *tmp; - unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; - - gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); - - if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); - free(sess->userlist_reply); - sess->userlist_reply = NULL; - goto fail; - } - - sess->userlist_reply = tmp; - sess->userlist_reply[len + h->length - 1] = 0; - memcpy(sess->userlist_reply + len, p + 1, h->length - 1); - } - - if (p[0] == GG_USERLIST_GET_MORE_REPLY) - break; - - e->type = GG_EVENT_USERLIST; - e->event.userlist.type = p[0]; - e->event.userlist.reply = sess->userlist_reply; - sess->userlist_reply = NULL; - - break; - } - - default: - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); - } - - free(h); - return 0; - -fail: - free(h); - return -1; -} - -/* - * gg_watch_fd() - * - * funkcja, kt�r� nale�y wywo�a�, gdy co� si� stanie z obserwowanym - * deskryptorem. zwraca klientowi informacj� o tym, co si� dzieje. - * - * - sess - opis sesji - * - * wska�nik do struktury gg_event, kt�r� trzeba zwolni� p��niej - * za pomoc� gg_event_free(). jesli rodzaj zdarzenia jest r�wny - * GG_EVENT_NONE, nale�y je zignorowa�. je�li zwr�ci�o NULL, - * sta�o si� co� niedobrego -- albo zabrak�o pami�ci albo zerwa�o - * po��czenie. - */ -struct gg_event *gg_watch_fd(struct gg_session *sess) -{ - struct gg_event *e; - int res = 0; - int port = 0; - int errno2 = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); - - if (!sess) { - errno = EFAULT; - return NULL; - } - - if (!(e = (void*) calloc(1, sizeof(*e)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); - return NULL; - } - - e->type = GG_EVENT_NONE; - - switch (sess->state) { - case GG_STATE_RESOLVING: - { - struct in_addr addr; - int failed = 0; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); - - if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); - failed = 1; - errno2 = errno; - } - - close(sess->fd); - sess->fd = -1; - -#ifndef __GG_LIBGADU_HAVE_PTHREAD - waitpid(sess->pid, NULL, 0); - sess->pid = -1; -#else - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#endif - - if (failed) { - errno = errno2; - goto fail_resolving; - } - - /* je�li jeste�my w resolverze i mamy ustawiony port - * proxy, znaczy, �e resolvowali�my proxy. zatem - * wpiszmy jego adres. */ - if (sess->proxy_port) - sess->proxy_addr = addr.s_addr; - - /* zapiszmy sobie adres huba i adres serwera (do - * bezpo�redniego po��czenia, je�li hub le�y) - * z resolvera. */ - if (sess->proxy_addr && sess->proxy_port) - port = sess->proxy_port; - else { - sess->server_addr = sess->hub_addr = addr.s_addr; - port = GG_APPMSG_PORT; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); - - /* ��czymy si� albo z hubem, albo z proxy, zale�nie - * od tego, co resolvowali�my. */ - if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { - /* je�li w trybie asynchronicznym gg_connect() - * zwr�ci b��d, nie ma sensu pr�bowa� dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); - goto fail_connecting; - } - - /* je�li podano serwer i ��czmy si� przez proxy, - * jest to bezpo�rednie po��czenie, inaczej jest - * do huba. */ - sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } - - case GG_STATE_CONNECTING_HUB: - { - char buf[1024], *client, *auth; - int res = 0, res_size = sizeof(res); - const char *host, *appmsg; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); - - /* je�li asynchroniczne, sprawdzamy, czy nie wyst�pi� - * przypadkiem jaki� b��d. */ - if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* no tak, nie uda�o si� po��czy� z proxy. nawet - * nie pr�bujemy dalej. */ - if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); - close(sess->fd); - - if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { - /* przy asynchronicznych, gg_connect() - * zwraca -1 przy b��dach socket(), - * ioctl(), braku routingu itd. dlatego - * nawet nie pr�bujemy dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); - - if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); - goto fail_connecting; - } - - if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) - host = "http://" GG_APPMSG_HOST; - else - host = ""; - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - appmsg = "appmsg3.asp"; - else -#endif - appmsg = "appmsg2.asp"; - - auth = gg_proxy_auth(); - - snprintf(buf, sizeof(buf) - 1, - "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" - "Host: " GG_APPMSG_HOST "\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Pragma: no-cache\r\n" - "%s" - "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); - - if (auth) - free(auth); - - free(client); - - /* zwolnij pami�� po wersji klienta. */ - if (sess->client_version) { - free(sess->client_version); - sess->client_version = NULL; - } - - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); - - /* zapytanie jest kr�tkie, wi�c zawsze zmie�ci si� - * do bufora gniazda. je�li write() zwr�ci mniej, - * sta�o si� co� z�ego. */ - if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); - - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_WRITING; - sess->state = GG_STATE_IDLE; - close(sess->fd); - sess->fd = -1; - break; - } - - sess->state = GG_STATE_READING_DATA; - sess->check = GG_CHECK_READ; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } - - case GG_STATE_READING_DATA: - { - char buf[1024], *tmp, *host; - int port = GG_DEFAULT_PORT; - struct in_addr addr; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); - - /* czytamy lini� z gniazda i obcinamy \r\n. */ - gg_read_line(sess->fd, buf, sizeof(buf) - 1); - gg_chomp(buf); - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); - - /* sprawdzamy, czy wszystko w porz�dku. */ - if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); - - close(sess->fd); - - /* je�li otrzymali�my jakie� dziwne informacje, - * pr�bujemy si� ��czy� z pomini�ciem huba. */ - if (sess->proxy_addr && sess->proxy_port) { - if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* trudno. nie wysz�o. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - sess->port = GG_DEFAULT_PORT; - - /* ��czymy si� na port 8074 huba. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - - sess->port = GG_HTTPS_PORT; - - /* ��czymy si� na port 443. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - /* ignorujemy reszt� nag��wka. */ - while (strcmp(buf, "\r\n") && strcmp(buf, "")) - gg_read_line(sess->fd, buf, sizeof(buf) - 1); - - /* czytamy pierwsz� lini� danych. */ - gg_read_line(sess->fd, buf, sizeof(buf) - 1); - gg_chomp(buf); - - /* je�li pierwsza liczba w linii nie jest r�wna zeru, - * oznacza to, �e mamy wiadomo�� systemow�. */ - if (atoi(buf)) { - char tmp[1024], *foo, *sysmsg_buf = NULL; - int len = 0; - - while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { - if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); - break; - } - - sysmsg_buf = foo; - - if (!len) - strcpy(sysmsg_buf, tmp); - else - strcat(sysmsg_buf, tmp); - - len += strlen(tmp); - } - - e->type = GG_EVENT_MSG; - e->event.msg.msgclass = atoi(buf); - e->event.msg.sender = 0; - e->event.msg.message = sysmsg_buf; - } - - close(sess->fd); - - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); - - /* analizujemy otrzymane dane. */ - tmp = buf; - - while (*tmp && *tmp != ' ') - tmp++; - while (*tmp && *tmp == ' ') - tmp++; - host = tmp; - while (*tmp && *tmp != ' ') - tmp++; - *tmp = 0; - - if ((tmp = (char*)strchr(host, ':'))) { - *tmp = 0; - port = atoi(tmp + 1); - } - - if (!strcmp(host, "notoperating")) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); - sess->fd = -1; - goto fail_unavailable; - } - - addr.s_addr = inet_addr(host); - sess->server_addr = addr.s_addr; - - if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { - /* je�li mamy proxy, ��czymy si� z nim. */ - if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* nie wysz�o? trudno. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - sess->port = port; - - /* ��czymy si� z w�a�ciwym serwerem. */ - if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - - sess->port = GG_HTTPS_PORT; - - /* nie wysz�o? pr�bujemy portu 443. */ - if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { - /* ostatnia deska ratunku zawiod�a? - * w takim razie zwijamy manatki. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } - - case GG_STATE_CONNECTING_GG: - { - int res = 0, res_size = sizeof(res); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); - - /* je�li wyst�pi� b��d podczas ��czenia si�... */ - if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* je�li nie uda�o si� po��czenie z proxy, - * nie mamy czego pr�bowa� wi�cej. */ - if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } - - close(sess->fd); - sess->fd = -1; - -#ifdef ETIMEDOUT - if (sess->timeout == 0) - errno = ETIMEDOUT; -#endif - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - /* je�li logujemy si� po TLS, nie pr�bujemy - * si� ��czy� ju� z niczym innym w przypadku - * b��du. nie do��, �e nie ma sensu, to i - * trzeba by si� bawi� w tworzenie na nowo - * SSL i SSL_CTX. */ - - if (sess->ssl) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } -#endif - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); - - sess->port = GG_HTTPS_PORT; - - /* pr�bujemy na port 443. */ - if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); - - if (gg_proxy_http_only) - sess->proxy_port = 0; - - /* je�li mamy proxy, wy�lijmy zapytanie. */ - if (sess->proxy_addr && sess->proxy_port) { - char buf[100], *auth = gg_proxy_auth(); - struct in_addr addr; - - if (sess->server_addr) - addr.s_addr = sess->server_addr; - else - addr.s_addr = sess->hub_addr; - - snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); - - /* wysy�amy zapytanie. jest ono na tyle kr�tkie, - * �e musi si� zmie�ci� w buforze gniazda. je�li - * write() zawiedzie, sta�o si� co� z�ego. */ - if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); - if (auth) - free(auth); - goto fail_connecting; - } - - if (auth) { - gg_debug(GG_DEBUG_MISC, "// %s", auth); - if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); - free(auth); - goto fail_connecting; - } - - free(auth); - } - - if (write(sess->fd, "\r\n", 2) < 2) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); - goto fail_connecting; - } - } - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) { - SSL_set_fd(sess->ssl, sess->fd); - - sess->state = GG_STATE_TLS_NEGOTIATION; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } -#endif - - sess->state = GG_STATE_READING_KEY; - sess->check = GG_CHECK_READ; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - case GG_STATE_TLS_NEGOTIATION: - { - int res; - X509 *peer; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); - - if ((res = SSL_connect(sess->ssl)) <= 0) { - int err = SSL_get_error(sess->ssl, res); - - if (res == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); - - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_TLS; - sess->state = GG_STATE_IDLE; - close(sess->fd); - sess->fd = -1; - break; - } - - if (err == SSL_ERROR_WANT_READ) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); - - sess->state = GG_STATE_TLS_NEGOTIATION; - sess->check = GG_CHECK_READ; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } else if (err == SSL_ERROR_WANT_WRITE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); - - sess->state = GG_STATE_TLS_NEGOTIATION; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } else { - char buf[1024]; - - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); - - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_TLS; - sess->state = GG_STATE_IDLE; - close(sess->fd); - sess->fd = -1; - break; - } - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); - - peer = SSL_get_peer_certificate(sess->ssl); - - if (!peer) - gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); - else { - char buf[1024]; - - X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); - - X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); - } - - sess->state = GG_STATE_READING_KEY; - sess->check = GG_CHECK_READ; - sess->timeout = GG_DEFAULT_TIMEOUT; - - break; - } -#endif - - case GG_STATE_READING_KEY: - { - struct gg_header *h; - struct gg_welcome *w; - struct gg_login60 l; - unsigned int hash; - unsigned char *password = sess->password; - int ret; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); - - memset(&l, 0, sizeof(l)); - l.dunno2 = 0xbe; - - /* XXX bardzo, bardzo, bardzo g�upi pomys� na pozbycie - * si� tekstu wrzucanego przez proxy. */ - if (sess->proxy_addr && sess->proxy_port) { - char buf[100]; - - strcpy(buf, ""); - gg_read_line(sess->fd, buf, sizeof(buf) - 1); - gg_chomp(buf); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); - - while (strcmp(buf, "")) { - gg_read_line(sess->fd, buf, sizeof(buf) - 1); - gg_chomp(buf); - if (strcmp(buf, "")) - gg_debug(GG_DEBUG_MISC, "// %s\n", buf); - } - - /* XXX niech czeka jeszcze raz w tej samej - * fazie. g�upio, ale dzia�a. */ - sess->proxy_port = 0; - - break; - } - - /* czytaj pierwszy pakiet. */ - if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); - - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_READING; - sess->state = GG_STATE_IDLE; - errno2 = errno; - close(sess->fd); - errno = errno2; - sess->fd = -1; - break; - } - - if (h->type != GG_WELCOME) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); - free(h); - close(sess->fd); - sess->fd = -1; - errno = EINVAL; - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_INVALID; - sess->state = GG_STATE_IDLE; - break; - } - - w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); - w->key = gg_fix32(w->key); - - hash = gg_login_hash(password, w->key); - - gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); - - free(h); - - free(sess->password); - sess->password = NULL; - - { - struct in_addr dcc_ip; - dcc_ip.s_addr = gg_dcc_ip; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); - } - - if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { - struct sockaddr_in sin; - int sin_len = sizeof(sin); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); - - if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); - l.local_ip = sin.sin_addr.s_addr; - } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); - l.local_ip = 0; - } - } else - l.local_ip = gg_dcc_ip; - - l.uin = gg_fix32(sess->uin); - l.hash = gg_fix32(hash); - l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); - l.version = gg_fix32(sess->protocol_version); - l.local_port = gg_fix16(gg_dcc_port); - l.image_size = sess->image_size; - - if (sess->external_addr && sess->external_port > 1023) { - l.external_ip = sess->external_addr; - l.external_port = gg_fix16(sess->external_port); - } - - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); - ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); - - free(sess->initial_descr); - sess->initial_descr = NULL; - - if (ret == -1) { - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sess->fd); - errno = errno2; - sess->fd = -1; - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_WRITING; - sess->state = GG_STATE_IDLE; - break; - } - - sess->state = GG_STATE_READING_REPLY; - - break; - } - - case GG_STATE_READING_REPLY: - { - struct gg_header *h; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); - - if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_READING; - sess->state = GG_STATE_IDLE; - errno2 = errno; - close(sess->fd); - errno = errno2; - sess->fd = -1; - break; - } - - if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); - e->type = GG_EVENT_CONN_SUCCESS; - sess->state = GG_STATE_CONNECTED; - sess->timeout = -1; - sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; - free(h); - break; - } - - if (h->type == GG_LOGIN_FAILED) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); - e->event.failure = GG_FAILURE_PASSWORD; - errno = EACCES; - } else if (h->type == GG_DISCONNECTING) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); - e->event.failure = GG_FAILURE_INTRUDER; - errno = EACCES; - } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); - e->event.failure = GG_FAILURE_INVALID; - errno = EINVAL; - } - - e->type = GG_EVENT_CONN_FAILED; - sess->state = GG_STATE_IDLE; - errno2 = errno; - close(sess->fd); - errno = errno2; - sess->fd = -1; - free(h); - - break; - } - - case GG_STATE_CONNECTED: - { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); - - sess->last_event = time(NULL); - - if ((res = gg_watch_fd_connected(sess, e)) == -1) { - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); - - if (errno == EAGAIN) { - e->type = GG_EVENT_NONE; - res = 0; - } else - res = -1; - } - break; - } - } - -done: - if (res == -1) { - free(e); - e = NULL; - } - - return e; - -fail_connecting: - if (sess->fd != -1) { - errno2 = errno; - close(sess->fd); - errno = errno2; - sess->fd = -1; - } - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_CONNECTING; - sess->state = GG_STATE_IDLE; - goto done; - -fail_resolving: - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_RESOLVING; - sess->state = GG_STATE_IDLE; - goto done; - -fail_unavailable: - e->type = GG_EVENT_CONN_FAILED; - e->event.failure = GG_FAILURE_UNAVAILABLE; - sess->state = GG_STATE_IDLE; - goto done; -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/http.c b/kopete/protocols/gadu/libgadu/http.c deleted file mode 100644 index 9f7369c5..00000000 --- a/kopete/protocols/gadu/libgadu/http.c +++ /dev/null @@ -1,522 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include "libgadu-config.h" - -#include <ctype.h> -#include <errno.h> -#include <netdb.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "compat.h" -#include "libgadu.h" - -/* - * gg_http_connect() // funkcja pomocnicza - * - * rozpoczyna po��czenie po http. - * - * - hostname - adres serwera - * - port - port serwera - * - async - asynchroniczne po��czenie - * - method - metoda http (GET, POST, cokolwiek) - * - path - �cie�ka do zasobu (musi by� poprzedzona ,,/'') - * - header - nag��wek zapytania plus ewentualne dane dla POST - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y - * zwolni� funkcj� gg_http_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header) -{ - struct gg_http *h; - - if (!hostname || !port || !method || !path || !header) { - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n"); - errno = EFAULT; - return NULL; - } - - if (!(h = malloc(sizeof(*h)))) - return NULL; - memset(h, 0, sizeof(*h)); - - h->async = async; - h->port = port; - h->fd = -1; - h->type = GG_SESSION_HTTP; - - if (gg_proxy_enabled) { - char *auth = gg_proxy_auth(); - - h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s", - method, hostname, port, path, (auth) ? auth : - "", header); - hostname = gg_proxy_host; - h->port = port = gg_proxy_port; - - if (auth) - free(auth); - } else { - h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s", - method, path, header); - } - - if (!h->query) { - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n"); - free(h); - errno = ENOMEM; - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query); - - if (async) { -#ifndef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve(&h->fd, &h->pid, hostname)) { -#else - if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n"); - gg_http_free(h); - errno = ENOENT; - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver); - - h->state = GG_STATE_RESOLVING; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - } else { - struct in_addr *hn, a; - - if (!(hn = gg_gethostbyname(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n"); - gg_http_free(h); - errno = ENOENT; - return NULL; - } else { - a.s_addr = hn->s_addr; - free(hn); - } - - if (!(h->fd = gg_connect(&a, port, 0)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - gg_http_free(h); - return NULL; - } - - h->state = GG_STATE_CONNECTING; - - while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) { - if (gg_http_watch_fd(h) == -1) - break; - } - - if (h->state != GG_STATE_PARSING) { - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n"); - gg_http_free(h); - return NULL; - } - } - - h->callback = gg_http_watch_fd; - h->destroy = gg_http_free; - - return h; -} - -#define gg_http_error(x) \ - close(h->fd); \ - h->fd = -1; \ - h->state = GG_STATE_ERROR; \ - h->error = x; \ - return 0; - -/* - * gg_http_watch_fd() - * - * przy asynchronicznej obs�udze HTTP funkcj� t� nale�y wywo�a�, je�li - * zmieni�o si� co� na obserwowanym deskryptorze. - * - * - h - struktura opisuj�ca po��czenie - * - * je�li wszystko posz�o dobrze to 0, inaczej -1. po��czenie b�dzie - * zako�czone, je�li h->state == GG_STATE_PARSING. je�li wyst�pi jaki� - * b��d, to b�dzie tam GG_STATE_ERROR i odpowiedni kod b��du w h->error. - */ -int gg_http_watch_fd(struct gg_http *h) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h); - - if (!h) { - gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (h->state == GG_STATE_RESOLVING) { - struct in_addr a; - - gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n"); - - if (read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) { - gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n"); - gg_http_error(GG_ERROR_RESOLVING); - } - - close(h->fd); - h->fd = -1; - -#ifndef __GG_LIBGADU_HAVE_PTHREAD - waitpid(h->pid, NULL, 0); -#else - if (h->resolver) { - pthread_cancel(*((pthread_t *) h->resolver)); - free(h->resolver); - h->resolver = NULL; - } -#endif - - gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port); - - if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno)); - gg_http_error(GG_ERROR_CONNECTING); - } - - h->state = GG_STATE_CONNECTING; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - - return 0; - } - - if (h->state == GG_STATE_CONNECTING) { - int res = 0; - unsigned int res_size = sizeof(res); - - if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno)); - close(h->fd); - h->fd = -1; - h->state = GG_STATE_ERROR; - h->error = GG_ERROR_CONNECTING; - if (res) - errno = res; - return 0; - } - - gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n"); - - h->state = GG_STATE_SENDING_QUERY; - } - - if (h->state == GG_STATE_SENDING_QUERY) { - int res; - - if ((res = write(h->fd, h->query, strlen(h->query))) < 1) { - gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno); - gg_http_error(GG_ERROR_WRITING); - } - - if (res < strlen(h->query)) { - gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res); - - memmove(h->query, h->query + res, strlen(h->query) - res + 1); - h->state = GG_STATE_SENDING_QUERY; - h->check = GG_CHECK_WRITE; - h->timeout = GG_DEFAULT_TIMEOUT; - } else { - gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query)); - free(h->query); - h->query = NULL; - - h->state = GG_STATE_READING_HEADER; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - } - - return 0; - } - - if (h->state == GG_STATE_READING_HEADER) { - char buf[1024], *tmp; - int res; - - if ((res = read(h->fd, buf, sizeof(buf))) == -1) { - gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno); - if (h->header) { - free(h->header); - h->header = NULL; - } - gg_http_error(GG_ERROR_READING); - } - - if (!res) { - gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n"); - if (h->header) { - free(h->header); - h->header = NULL; - } - gg_http_error(GG_ERROR_READING); - } - - gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res); - - if (!(tmp = realloc(h->header, h->header_size + res + 1))) { - gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n"); - free(h->header); - h->header = NULL; - gg_http_error(GG_ERROR_READING); - } - - h->header = tmp; - - memcpy(h->header + h->header_size, buf, res); - h->header_size += res; - - gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size); - - h->header[h->header_size] = 0; - - if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) { - int sep_len = (*tmp == '\r') ? 4 : 2; - unsigned int left; - char *line; - - left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len); - - gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left); - - /* HTTP/1.1 200 OK */ - if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) { - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); - - gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n"); - free(h->header); - h->header = NULL; - gg_http_error(GG_ERROR_CONNECTING); - } - - h->body_size = 0; - line = h->header; - *tmp = 0; - - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); - - while (line) { - if (!strncasecmp(line, "Content-length: ", 16)) { - h->body_size = atoi(line + 16); - } - line = (char*)strchr(line, '\n'); - if (line) - line++; - } - - if (h->body_size <= 0) { - gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n"); - h->body_size = left; - } - - if (left > h->body_size) { - gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left); - h->body_size = left; - } - - gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size); - - if (!(h->body = malloc(h->body_size + 1))) { - gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1); - free(h->header); - h->header = NULL; - gg_http_error(GG_ERROR_READING); - } - - if (left) { - memcpy(h->body, tmp + sep_len, left); - h->body_done = left; - } - - h->body[left] = 0; - - h->state = GG_STATE_READING_DATA; - h->check = GG_CHECK_READ; - h->timeout = GG_DEFAULT_TIMEOUT; - } - - return 0; - } - - if (h->state == GG_STATE_READING_DATA) { - char buf[1024]; - int res; - - if ((res = read(h->fd, buf, sizeof(buf))) == -1) { - gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno); - if (h->body) { - free(h->body); - h->body = NULL; - } - gg_http_error(GG_ERROR_READING); - } - - if (!res) { - if (h->body_done >= h->body_size) { - gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n"); - h->state = GG_STATE_PARSING; - close(h->fd); - h->fd = -1; - } else { - gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size); - if (h->body) { - free(h->body); - h->body = NULL; - } - gg_http_error(GG_ERROR_READING); - } - - return 0; - } - - gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res); - - if (h->body_done + res > h->body_size) { - char *tmp; - - gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size); - - if (!(tmp = realloc(h->body, h->body_done + res + 1))) { - gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1); - free(h->body); - h->body = NULL; - gg_http_error(GG_ERROR_READING); - } - - h->body = tmp; - h->body_size = h->body_done + res; - } - - h->body[h->body_done + res] = 0; - memcpy(h->body + h->body_done, buf, res); - h->body_done += res; - - gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size); - - return 0; - } - - if (h->fd != -1) - close(h->fd); - - h->fd = -1; - h->state = GG_STATE_ERROR; - h->error = 0; - - return -1; -} - -#undef gg_http_error - -/* - * gg_http_stop() - * - * je�li po��czenie jest w trakcie, przerywa je. nie zwalnia h->data. - * - * - h - struktura opisuj�ca po��czenie - */ -void gg_http_stop(struct gg_http *h) -{ - if (!h) - return; - - if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE) - return; - - if (h->fd != -1) - close(h->fd); - h->fd = -1; -} - -/* - * gg_http_free_fields() // funkcja wewn�trzna - * - * zwalnia pola struct gg_http, ale nie zwalnia samej struktury. - */ -void gg_http_free_fields(struct gg_http *h) -{ - if (!h) - return; - - if (h->body) { - free(h->body); - h->body = NULL; - } - - if (h->query) { - free(h->query); - h->query = NULL; - } - - if (h->header) { - free(h->header); - h->header = NULL; - } -} - -/* - * gg_http_free() - * - * pr�buje zamkn�� po��czenie i zwalnia pami�� po nim. - * - * - h - struktura, kt�r� nale�y zlikwidowa� - */ -void gg_http_free(struct gg_http *h) -{ - if (!h) - return; - - gg_http_stop(h); - gg_http_free_fields(h); - free(h); -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/libgadu-config.h.in b/kopete/protocols/gadu/libgadu/libgadu-config.h.in deleted file mode 100644 index dc4fb435..00000000 --- a/kopete/protocols/gadu/libgadu/libgadu-config.h.in +++ /dev/null @@ -1,30 +0,0 @@ -/* Local libgadu configuration. */ - -#ifndef __GG_LIBGADU_CONFIG_H -#define __GG_LIBGADU_CONFIG_H - -/* Defined if libgadu was compiled for bigendian machine. */ -#undef __GG_LIBGADU_BIGENDIAN - -/* Defined if libgadu was compiled and linked with pthread support. */ -#define __GG_LIBGADU_HAVE_PTHREAD - -/* Defined if this machine has C99-compiliant vsnprintf(). */ -#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF - -/* Defined if this machine has va_copy(). */ -#undef __GG_LIBGADU_HAVE_VA_COPY - -/* Defined if this machine has __va_copy(). */ -#undef __GG_LIBGADU_HAVE___VA_COPY - -/* Defined if this machine supports long long. */ -#undef __GG_LIBGADU_HAVE_LONG_LONG - -/* Defined if libgadu was compiled and linked with TLS support. */ -#undef __GG_LIBGADU_HAVE_OPENSSL - -/* Include file containing uintXX_t declarations. */ -#include <inttypes.h> - -#endif /* __GG_LIBGADU_CONFIG_H */ diff --git a/kopete/protocols/gadu/libgadu/libgadu.c b/kopete/protocols/gadu/libgadu/libgadu.c deleted file mode 100644 index 79f0a170..00000000 --- a/kopete/protocols/gadu/libgadu/libgadu.c +++ /dev/null @@ -1,1818 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo¼ny <speedy@ziew.org> - * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> - * Tomasz Chiliñski <chilek@chilan.com> - * Adam Wysocki <gophi@ekg.chmurka.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif - -#include "libgadu-config.h" - -#include <errno.h> -#include <netdb.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <signal.h> -#include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL -# include <openssl/err.h> -# include <openssl/rand.h> -#endif - -#include "compat.h" -#include "libgadu.h" - -int gg_debug_level = 0; -void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; - -int gg_dcc_port = 0; -unsigned long gg_dcc_ip = 0; - -unsigned long gg_local_ip = 0; -/* - * zmienne opisuj±ce parametry proxy http. - */ -char *gg_proxy_host = NULL; -int gg_proxy_port = 0; -int gg_proxy_enabled = 0; -int gg_proxy_http_only = 0; -char *gg_proxy_username = NULL; -char *gg_proxy_password = NULL; - -#ifndef lint -static char rcsid[] -#ifdef __GNUC__ -__attribute__ ((unused)) -#endif -= "$Id$"; -#endif - -/* - * gg_libgadu_version() - * - * zwraca wersjê libgadu. - * - * - brak - * - * wersja libgadu. - */ -const char *gg_libgadu_version() -{ - return GG_LIBGADU_VERSION; -} - -/* - * gg_fix32() - * - * zamienia kolejno¶æ bajtów w liczbie 32-bitowej tak, by odpowiada³a - * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera, - * zamienia tylko na maszynach BE-wych. - * - * - x - liczba do zamiany - * - * liczba z odpowiedni± kolejno¶ci± bajtów. - */ -uint32_t gg_fix32(uint32_t x) -{ -#ifndef __GG_LIBGADU_BIGENDIAN - return x; -#else - return (uint32_t) - (((x & (uint32_t) 0x000000ffU) << 24) | - ((x & (uint32_t) 0x0000ff00U) << 8) | - ((x & (uint32_t) 0x00ff0000U) >> 8) | - ((x & (uint32_t) 0xff000000U) >> 24)); -#endif -} - -/* - * gg_fix16() - * - * zamienia kolejno¶æ bajtów w liczbie 16-bitowej tak, by odpowiada³a - * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera, - * zamienia tylko na maszynach BE-wych. - * - * - x - liczba do zamiany - * - * liczba z odpowiedni± kolejno¶ci± bajtów. - */ -uint16_t gg_fix16(uint16_t x) -{ -#ifndef __GG_LIBGADU_BIGENDIAN - return x; -#else - return (uint16_t) - (((x & (uint16_t) 0x00ffU) << 8) | - ((x & (uint16_t) 0xff00U) >> 8)); -#endif -} - -/* - * gg_login_hash() // funkcja wewnêtrzna - * - * liczy hash z has³a i danego seeda. - * - * - password - has³o do hashowania - * - seed - warto¶æ podana przez serwer - * - * hash. - */ -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) -{ - unsigned int x, y, z; - - y = seed; - - for (x = 0; *password; password++) { - x = (x & 0xffffff00) | *password; - y ^= x; - y += x; - x <<= 8; - y ^= x; - x <<= 8; - y -= x; - x <<= 8; - y ^= x; - - z = y & 0x1F; - y = (y << z) | (y >> (32 - z)); - } - - return y; -} - -/* - * gg_resolve() // funkcja wewnêtrzna - * - * tworzy potok, forkuje siê i w drugim procesie zaczyna resolvowaæ - * podanego hosta. zapisuje w sesji deskryptor potoku. je¶li co¶ tam - * bêdzie gotowego, znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li - * nie znajdzie, zwraca INADDR_NONE. - * - * - fd - wska¼nik gdzie wrzuciæ deskryptor - * - pid - gdzie wrzuciæ pid procesu potomnego - * - hostname - nazwa hosta do zresolvowania - * - * 0, -1. - */ -int gg_resolve(int *fd, int *pid, const char *hostname) -{ - int pipes[2], res; - struct in_addr a; - int errno2; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); - - if (!fd || !pid) { - errno = EFAULT; - return -1; - } - - if (pipe(pipes) == -1) - return -1; - - if ((res = fork()) == -1) { - errno2 = errno; - close(pipes[0]); - close(pipes[1]); - errno = errno2; - return -1; - } - - if (!res) { - close(pipes[0]); - - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(pipes[1], &a, sizeof(a)); - - exit(0); - } - - close(pipes[1]); - - *fd = pipes[0]; - *pid = res; - - return 0; -} - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - -struct gg_resolve_pthread_data { - char *hostname; - int fd; -}; - -static void *gg_resolve_pthread_thread(void *arg) -{ - struct gg_resolve_pthread_data *d = arg; - struct in_addr a; - - pthread_detach(pthread_self()); - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - pthread_exit(NULL); - - return NULL; /* ¿eby kompilator nie marudzi³ */ -} - -/* - * gg_resolve_pthread() // funkcja wewnêtrzna - * - * tworzy potok, nowy w±tek i w nim zaczyna resolvowaæ podanego hosta. - * zapisuje w sesji deskryptor potoku. je¶li co¶ tam bêdzie gotowego, - * znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li nie znajdzie, zwraca - * INADDR_NONE. - * - * - fd - wska¼nik do zmiennej przechowuj±cej desktyptor resolvera - * - resolver - wska¼nik do wska¼nika resolvera - * - hostname - nazwa hosta do zresolvowania - * - * 0, -1. - */ -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_pthread_data *d = NULL; - pthread_t *tmp; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (!(tmp = malloc(sizeof(pthread_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); - return -1; - } - - if (pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - free(tmp); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->fd = pipes[1]; - - if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); - - *resolver = tmp; - - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - free(tmp); - - errno = new_errno; - - return -1; -} - -#endif - -/* - * gg_read() // funkcja pomocnicza - * - * czyta z gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy - * po³±czenie zwyk³e czy TLS. - * - * - sess - sesja, - * - buf - bufor, - * - length - ilo¶æ bajtów, - * - * takie same warto¶ci jak read(). - */ -int gg_read(struct gg_session *sess, char *buf, int length) -{ - int res; - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) { - int err; - - res = SSL_read(sess->ssl, buf, length); - - if (res < 0) { - err = SSL_get_error(sess->ssl, res); - - if (err == SSL_ERROR_WANT_READ) - errno = EAGAIN; - - return -1; - } - } else -#endif - res = read(sess->fd, buf, length); - - return res; -} - -/* - * gg_write() // funkcja pomocnicza - * - * zapisuje do gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy - * po³±czenie zwyk³e czy TLS. - * - * - sess - sesja, - * - buf - bufor, - * - length - ilo¶æ bajtów, - * - * takie same warto¶ci jak write(). - */ -int gg_write(struct gg_session *sess, const char *buf, int length) -{ - int res = 0; - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) { - int err; - - res = SSL_write(sess->ssl, buf, length); - - if (res < 0) { - err = SSL_get_error(sess->ssl, res); - - if (err == SSL_ERROR_WANT_WRITE) - errno = EAGAIN; - - return -1; - } - } else -#endif - { - int written = 0; - - while (written < length) { - res = write(sess->fd, buf + written, length - written); - - if (res == -1) { - if (errno == EAGAIN) - continue; - else - break; - } else { - written += res; - res = written; - } - } - } - - return res; -} - -/* - * gg_recv_packet() // funkcja wewnêtrzna - * - * odbiera jeden pakiet i zwraca wska¼nik do niego. pamiêæ po nim - * nale¿y zwolniæ za pomoc± free(). - * - * - sess - opis sesji - * - * w przypadku b³êdu NULL, kod b³êdu w errno. nale¿y zwróciæ uwagê, ¿e gdy - * po³±czenie jest nieblokuj±ce, a kod b³êdu wynosi EAGAIN, nie uda³o siê - * odczytaæ ca³ego pakietu i nie nale¿y tego traktowaæ jako b³±d. - */ -void *gg_recv_packet(struct gg_session *sess) -{ - struct gg_header h; - char *buf = NULL; - int ret = 0; - unsigned int offset, size = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); - - if (!sess) { - errno = EFAULT; - return NULL; - } - - if (sess->recv_left < 1) { - if (sess->header_buf) { - memcpy(&h, sess->header_buf, sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); - free(sess->header_buf); - sess->header_buf = NULL; - } else - sess->header_done = 0; - - while (sess->header_done < sizeof(h)) { - ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); - - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); - - if (!ret) { - errno = ECONNRESET; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); - return NULL; - } - - if (ret == -1) { - if (errno == EINTR) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); - continue; - } - - if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); - - if (!(sess->header_buf = malloc(sess->header_done))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); - return NULL; - } - - memcpy(sess->header_buf, &h, sess->header_done); - - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); - - return NULL; - } - - sess->header_done += ret; - - } - - h.type = gg_fix32(h.type); - h.length = gg_fix32(h.length); - } else - memcpy(&h, sess->recv_buf, sizeof(h)); - - /* jakie¶ sensowne limity na rozmiar pakietu */ - if (h.length > 65535) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); - errno = ERANGE; - return NULL; - } - - if (sess->recv_left > 0) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); - size = sess->recv_left; - offset = sess->recv_done; - buf = sess->recv_buf; - } else { - if (!(buf = malloc(sizeof(h) + h.length + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); - return NULL; - } - - memcpy(buf, &h, sizeof(h)); - - offset = 0; - size = h.length; - } - - while (size > 0) { - ret = gg_read(sess, buf + sizeof(h) + offset, size); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); - if (!ret) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); - errno = ECONNRESET; - return NULL; - } - if (ret > -1 && ret <= size) { - offset += ret; - size -= ret; - } else if (ret == -1) { - int errno2 = errno; - - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno = errno2; - - if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); - sess->recv_buf = buf; - sess->recv_left = size; - sess->recv_done = offset; - return NULL; - } - if (errno != EINTR) { - free(buf); - return NULL; - } - } - } - - sess->recv_left = 0; - - if ((gg_debug_level & GG_DEBUG_DUMP)) { - unsigned int i; - - gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); - for (i = 0; i < sizeof(h) + h.length; i++) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); - } - - return buf; -} - -/* - * gg_send_packet() // funkcja wewnêtrzna - * - * konstruuje pakiet i wysy³a go do serwera. - * - * - sock - deskryptor gniazda - * - type - typ pakietu - * - payload_1 - pierwsza czê¶æ pakietu - * - payload_length_1 - d³ugo¶æ pierwszej czê¶ci - * - payload_2 - druga czê¶æ pakietu - * - payload_length_2 - d³ugo¶æ drugiej czê¶ci - * - ... - kolejne czê¶ci pakietu i ich d³ugo¶ci - * - NULL - koñcowym parametr (konieczny!) - * - * je¶li siê powiod³o, zwraca 0, w przypadku b³êdu -1. je¶li errno == ENOMEM, - * zabrak³o pamiêci. inaczej by³ b³±d przy wysy³aniu pakietu. dla errno == 0 - * nie wys³ano ca³ego pakietu. - */ -int gg_send_packet(struct gg_session *sess, int type, ...) -{ - struct gg_header *h; - char *tmp; - unsigned int tmp_length; - void *payload; - unsigned int payload_length; - va_list ap; - int res; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); - - tmp_length = sizeof(struct gg_header); - - if (!(tmp = malloc(tmp_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); - return -1; - } - - va_start(ap, type); - - payload = va_arg(ap, void *); - - while (payload) { - char *tmp2; - - payload_length = va_arg(ap, unsigned int); - - if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); - free(tmp); - va_end(ap); - return -1; - } - - tmp = tmp2; - - memcpy(tmp + tmp_length, payload, payload_length); - tmp_length += payload_length; - - payload = va_arg(ap, void *); - } - - va_end(ap); - - h = (struct gg_header*) tmp; - h->type = gg_fix32(type); - h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); - - if ((gg_debug_level & GG_DEBUG_DUMP)) { - unsigned int i; - - gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); - for (i = 0; i < tmp_length; ++i) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); - } - - if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); - free(tmp); - return -1; - } - - free(tmp); - return 0; -} - -/* - * gg_session_callback() // funkcja wewnêtrzna - * - * wywo³ywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje - * do gg_session->event jego wynik. - */ -static int gg_session_callback(struct gg_session *s) -{ - if (!s) { - errno = EFAULT; - return -1; - } - - return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; -} - -/* - * gg_login() - * - * rozpoczyna procedurê ³±czenia siê z serwerem. resztê obs³uguje siê przez - * gg_watch_fd(). - * - * UWAGA! program musi obs³u¿yæ SIGCHLD, je¶li ³±czy siê asynchronicznie, - * ¿eby poprawnie zamkn±æ proces resolvera. - * - * - p - struktura opisuj±ca pocz±tkowy stan. wymagane pola: uin, - * password - * - * w przypadku b³êdu NULL, je¶li idzie dobrze (async) albo posz³o - * dobrze (sync), zwróci wska¼nik do zaalokowanej struct gg_session. - */ -struct gg_session *gg_login(const struct gg_login_params *p) -{ - struct gg_session *sess = NULL; - char *hostname; - int port; - - if (!p) { - gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p); - errno = EFAULT; - return NULL; - } - - gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async); - - if (!(sess = malloc(sizeof(struct gg_session)))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n"); - goto fail; - } - - memset(sess, 0, sizeof(struct gg_session)); - - if (!p->password || !p->uin) { - gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n"); - errno = EFAULT; - goto fail; - } - - if (!(sess->password = strdup(p->password))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n"); - goto fail; - } - - if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); - goto fail; - } - - sess->uin = p->uin; - sess->state = GG_STATE_RESOLVING; - sess->check = GG_CHECK_READ; - sess->timeout = GG_DEFAULT_TIMEOUT; - sess->async = p->async; - sess->type = GG_SESSION_GG; - sess->initial_status = p->status; - sess->callback = gg_session_callback; - sess->destroy = gg_free_session; - sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); - sess->server_addr = p->server_addr; - sess->external_port = p->external_port; - sess->external_addr = p->external_addr; - sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; - if (p->era_omnix) - sess->protocol_version |= GG_ERA_OMNIX_MASK; - if (p->has_audio) - sess->protocol_version |= GG_HAS_AUDIO_MASK; - sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; - sess->last_sysmsg = p->last_sysmsg; - sess->image_size = p->image_size; - sess->pid = -1; - - if (p->tls == 1) { -#ifdef __GG_LIBGADU_HAVE_OPENSSL - char buf[1024]; - - OpenSSL_add_ssl_algorithms(); - - if (!RAND_status()) { - char rdata[1024]; - struct { - time_t time; - void *ptr; - } rstruct; - - time(&rstruct.time); - rstruct.ptr = (void *) &rstruct; - - RAND_seed((void *) rdata, sizeof(rdata)); - RAND_seed((void *) &rstruct, sizeof(rstruct)); - } - - sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); - - if (!sess->ssl_ctx) { - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); - goto fail; - } - - SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL); - - sess->ssl = SSL_new(sess->ssl_ctx); - - if (!sess->ssl) { - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); - goto fail; - } -#else - gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); -#endif - } - - if (gg_proxy_enabled) { - hostname = gg_proxy_host; - sess->proxy_port = port = gg_proxy_port; - } else { - hostname = GG_APPMSG_HOST; - port = GG_APPMSG_PORT; - } - - if (!p->async) { - struct in_addr a; - - if (!p->server_addr || !p->server_port) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); - goto fail; - } else { - a.s_addr = hn->s_addr; - free(hn); - } - } - } else { - a.s_addr = p->server_addr; - port = p->server_port; - } - - sess->hub_addr = a.s_addr; - - if (gg_proxy_enabled) - sess->proxy_addr = a.s_addr; - - if ((sess->fd = gg_connect(&a, port, 0)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; - } - - if (p->server_addr && p->server_port) - sess->state = GG_STATE_CONNECTING_GG; - else - sess->state = GG_STATE_CONNECTING_HUB; - - while (sess->state != GG_STATE_CONNECTED) { - struct gg_event *e; - - if (!(e = gg_watch_fd(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n"); - goto fail; - } - - if (e->type == GG_EVENT_CONN_FAILED) { - errno = EACCES; - gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n"); - gg_event_free(e); - goto fail; - } - - gg_event_free(e); - } - - return sess; - } - - if (!sess->server_addr || gg_proxy_enabled) { -#ifndef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve(&sess->fd, &sess->pid, hostname)) { -#else - if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; - } - } else { - if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; - } - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - } - - return sess; - -fail: - if (sess) { - if (sess->password) - free(sess->password); - if (sess->initial_descr) - free(sess->initial_descr); - free(sess); - } - - return NULL; -} - -/* - * gg_free_session() - * - * próbuje zamkn±æ po³±czenia i zwalnia pamiêæ zajmowan± przez sesjê. - * - * - sess - opis sesji - */ -void gg_free_session(struct gg_session *sess) -{ - if (!sess) - return; - - /* XXX dopisaæ zwalnianie i zamykanie wszystkiego, co mog³o zostaæ */ - - if (sess->password) - free(sess->password); - - if (sess->initial_descr) - free(sess->initial_descr); - - if (sess->client_version) - free(sess->client_version); - - if (sess->header_buf) - free(sess->header_buf); - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - SSL_free(sess->ssl); - - if (sess->ssl_ctx) - SSL_CTX_free(sess->ssl_ctx); -#endif - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#else - if (sess->pid != -1) { - kill(sess->pid, SIGTERM); - waitpid(sess->pid, NULL, WNOHANG); - } -#endif - - if (sess->fd != -1) - close(sess->fd); - - while (sess->images) - gg_image_queue_remove(sess, sess->images, 1); - - free(sess); -} - -/* - * gg_change_status() - * - * zmienia status u¿ytkownika. przydatne do /away i /busy oraz /quit. - * - * - sess - opis sesji - * - status - nowy status u¿ytkownika - * - * 0, -1. - */ -int gg_change_status(struct gg_session *sess, int status) -{ - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); -} - -/* - * gg_change_status_descr() - * - * zmienia status u¿ytkownika na opisowy. - * - * - sess - opis sesji - * - status - nowy status u¿ytkownika - * - descr - opis statusu - * - * 0, -1. - */ -int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) -{ - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - - if (!sess || !descr) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); -} - -/* - * gg_change_status_descr_time() - * - * zmienia status u¿ytkownika na opisowy z godzin± powrotu. - * - * - sess - opis sesji - * - status - nowy status u¿ytkownika - * - descr - opis statusu - * - time - czas w formacie uniksowym - * - * 0, -1. - */ -int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) -{ - struct gg_new_status p; - uint32_t newtime; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); - - if (!sess || !descr || !time) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - newtime = gg_fix32(time); - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); -} - -/* - * gg_logoff() - * - * wylogowuje u¿ytkownika i zamyka po³±czenie, ale nie zwalnia pamiêci. - * - * - sess - opis sesji - */ -void gg_logoff(struct gg_session *sess) -{ - if (!sess) - return; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); - - if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) - gg_change_status(sess, GG_STATUS_NOT_AVAIL); - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - SSL_shutdown(sess->ssl); -#endif - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#else - if (sess->pid != -1) { - kill(sess->pid, SIGTERM); - waitpid(sess->pid, NULL, WNOHANG); - sess->pid = -1; - } -#endif - - if (sess->fd != -1) { - shutdown(sess->fd, SHUT_RDWR); - close(sess->fd); - sess->fd = -1; - } -} - -/* - * gg_image_request() - * - * wysy³a ¿±danie wys³ania obrazka o podanych parametrach. - * - * - sess - opis sesji - * - recipient - numer adresata - * - size - rozmiar obrazka - * - crc32 - suma kontrolna obrazka - * - * 0/-1 - */ -int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) -{ - struct gg_send_msg s; - struct gg_msg_image_request r; - char dummy = 0; - int res; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (size < 0) { - errno = EINVAL; - return -1; - } - - s.recipient = gg_fix32(recipient); - s.seq = gg_fix32(0); - s.msgclass = gg_fix32(GG_CLASS_MSG); - - r.flag = 0x04; - r.size = gg_fix32(size); - r.crc32 = gg_fix32(crc32); - - res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); - - if (!res) { - struct gg_image_queue *q = malloc(sizeof(*q)); - char *buf; - - if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); - return -1; - } - - buf = malloc(size); - if (size && !buf) - { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); - free(q); - return -1; - } - - memset(q, 0, sizeof(*q)); - - q->sender = recipient; - q->size = size; - q->crc32 = crc32; - q->image = buf; - - if (!sess->images) - sess->images = q; - else { - struct gg_image_queue *qq; - - for (qq = sess->images; qq->next; qq = qq->next) - ; - - qq->next = q; - } - } - - return res; -} - -/* - * gg_image_reply() - * - * wysy³a ¿±dany obrazek. - * - * - sess - opis sesji - * - recipient - numer adresata - * - filename - nazwa pliku - * - image - bufor z obrazkiem - * - size - rozmiar obrazka - * - * 0/-1 - */ -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) -{ - struct gg_msg_image_reply *r; - struct gg_send_msg s; - const char *tmp; - char buf[1910]; - int res = -1; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); - - if (!sess || !filename || !image) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (size < 0) { - errno = EINVAL; - return -1; - } - - /* wytnij ¶cie¿ki, zostaw tylko nazwê pliku */ - while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) - filename = tmp + 1; - - if (strlen(filename) < 1 || strlen(filename) > 1024) { - errno = EINVAL; - return -1; - } - - s.recipient = gg_fix32(recipient); - s.seq = gg_fix32(0); - s.msgclass = gg_fix32(GG_CLASS_MSG); - - buf[0] = 0; - r = (void*) &buf[1]; - - r->flag = 0x05; - r->size = gg_fix32(size); - r->crc32 = gg_fix32(gg_crc32(0, image, size)); - - while (size > 0) { - int buflen, chunklen; - - /* \0 + struct gg_msg_image_reply */ - buflen = sizeof(struct gg_msg_image_reply) + 1; - - /* w pierwszym kawa³ku jest nazwa pliku */ - if (r->flag == 0x05) { - strcpy(buf + buflen, filename); - buflen += strlen(filename) + 1; - } - - chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size; - - memcpy(buf + buflen, image, chunklen); - size -= chunklen; - image += chunklen; - - res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); - - if (res == -1) - break; - - r->flag = 0x06; - } - - return res; -} - -/* - * gg_send_message_ctcp() - * - * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer - * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo¶ci - * - recipient - numer adresata - * - message - tre¶æ wiadomo¶ci - * - message_len - d³ugo¶æ - * - * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. - */ -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) -{ - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - s.recipient = gg_fix32(recipient); - s.seq = gg_fix32(0); - s.msgclass = gg_fix32(msgclass); - - return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); -} - -/* - * gg_send_message() - * - * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer - * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo¶ci - * - recipient - numer adresata - * - message - tre¶æ wiadomo¶ci - * - * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. - */ -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); - - return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); -} - -/* - * gg_send_message_richtext() - * - * wysy³a kolorow± wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer - * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo¶ci - * - recipient - numer adresata - * - message - tre¶æ wiadomo¶ci - * - format - informacje o formatowaniu - * - formatlen - d³ugo¶æ informacji o formatowaniu - * - * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. - */ -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!message) { - errno = EFAULT; - return -1; - } - - s.recipient = gg_fix32(recipient); - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1) - return -1; - - return gg_fix32(s.seq); -} - -/* - * gg_send_message_confer() - * - * wysy³a wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca losowy numer - * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo¶ci - * - recipients_count - ilo¶æ adresatów - * - recipients - numerki adresatów - * - message - tre¶æ wiadomo¶ci - * - * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. - */ -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); - - return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); -} - -/* - * gg_send_message_confer_richtext() - * - * wysy³a kolorow± wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca - * losowy numer sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do - * potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo¶ci - * - recipients_count - ilo¶æ adresatów - * - recipients - numerki adresatów - * - message - tre¶æ wiadomo¶ci - * - format - informacje o formatowaniu - * - formatlen - d³ugo¶æ informacji o formatowaniu - * - * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. - */ -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - struct gg_msg_recipients r; - int i, j, k; - uin_t *recps; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { - errno = EINVAL; - return -1; - } - - r.flag = 0x01; - r.count = gg_fix32(recipients_count - 1); - - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - - recps = malloc(sizeof(uin_t) * recipients_count); - if (!recps) - return -1; - - for (i = 0; i < recipients_count; i++) { - - s.recipient = gg_fix32(recipients[i]); - - for (j = 0, k = 0; j < recipients_count; j++) - if (recipients[j] != recipients[i]) { - recps[k] = gg_fix32(recipients[j]); - k++; - } - - if (!i) - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { - free(recps); - return -1; - } - } - - free(recps); - - return gg_fix32(s.seq); -} - -/* - * gg_ping() - * - * wysy³a do serwera pakiet ping. - * - * - sess - opis sesji - * - * 0, -1. - */ -int gg_ping(struct gg_session *sess) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - return gg_send_packet(sess, GG_PING, NULL); -} - -/* - * gg_notify_ex() - * - * wysy³a serwerowi listê kontaktów (wraz z odpowiadaj±cymi im typami userów), - * dziêki czemu wie, czyj stan nas interesuje. - * - * - sess - opis sesji - * - userlist - wska¼nik do tablicy numerów - * - types - wska¼nik do tablicy typów u¿ytkowników - * - count - ilo¶æ numerków - * - * 0, -1. - */ -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) -{ - struct gg_notify *n; - uin_t *u; - char *t; - int i, res = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!userlist || !count) - return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - - while (count > 0) { - int part_count, packet_type; - - if (count > 400) { - part_count = 400; - packet_type = GG_NOTIFY_FIRST; - } else { - part_count = count; - packet_type = GG_NOTIFY_LAST; - } - - if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) - return -1; - - for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { - n[i].uin = gg_fix32(*u); - n[i].dunno1 = *t; - } - - if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { - free(n); - res = -1; - break; - } - - count -= part_count; - userlist += part_count; - types += part_count; - - free(n); - } - - return res; -} - -/* - * gg_notify() - * - * wysy³a serwerowi listê kontaktów, dziêki czemu wie, czyj stan nas - * interesuje. - * - * - sess - opis sesji - * - userlist - wska¼nik do tablicy numerów - * - count - ilo¶æ numerków - * - * 0, -1. - */ -int gg_notify(struct gg_session *sess, uin_t *userlist, int count) -{ - struct gg_notify *n; - uin_t *u; - int i, res = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!userlist || !count) - return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - - while (count > 0) { - int part_count, packet_type; - - if (count > 400) { - part_count = 400; - packet_type = GG_NOTIFY_FIRST; - } else { - part_count = count; - packet_type = GG_NOTIFY_LAST; - } - - if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) - return -1; - - for (u = userlist, i = 0; i < part_count; u++, i++) { - n[i].uin = gg_fix32(*u); - n[i].dunno1 = GG_USER_NORMAL; - } - - if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { - res = -1; - free(n); - break; - } - - free(n); - - userlist += part_count; - count -= part_count; - } - - return res; -} - -/* - * gg_add_notify_ex() - * - * dodaje do listy kontaktów dany numer w trakcie po³±czenia. - * dodawanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html) - * - * - sess - opis sesji - * - uin - numer - * - type - typ - * - * 0, -1. - */ -int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) -{ - struct gg_add_remove a; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - a.uin = gg_fix32(uin); - a.dunno1 = type; - - return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); -} - -/* - * gg_add_notify() - * - * dodaje do listy kontaktów dany numer w trakcie po³±czenia. - * - * - sess - opis sesji - * - uin - numer - * - * 0, -1. - */ -int gg_add_notify(struct gg_session *sess, uin_t uin) -{ - return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); -} - -/* - * gg_remove_notify_ex() - * - * usuwa z listy kontaktów w trakcie po³±czenia. - * usuwanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html) - * - * - sess - opis sesji - * - uin - numer - * - type - typ - * - * 0, -1. - */ -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) -{ - struct gg_add_remove a; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - a.uin = gg_fix32(uin); - a.dunno1 = type; - - return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); -} - -/* - * gg_remove_notify() - * - * usuwa z listy kontaktów w trakcie po³±czenia. - * - * - sess - opis sesji - * - uin - numer - * - * 0, -1. - */ -int gg_remove_notify(struct gg_session *sess, uin_t uin) -{ - return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); -} - -/* - * gg_userlist_request() - * - * wysy³a ¿±danie/zapytanie listy kontaktów na serwerze. - * - * - sess - opis sesji - * - type - rodzaj zapytania/¿±dania - * - request - tre¶æ zapytania/¿±dania (mo¿e byæ NULL) - * - * 0, -1 - */ -int gg_userlist_request(struct gg_session *sess, char type, const char *request) -{ - int len; - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!request) { - sess->userlist_blocks = 1; - return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); - } - - len = strlen(request); - - sess->userlist_blocks = 0; - - while (len > 2047) { - sess->userlist_blocks++; - - if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) - return -1; - - if (type == GG_USERLIST_PUT) - type = GG_USERLIST_PUT_MORE; - - request += 2047; - len -= 2047; - } - - sess->userlist_blocks++; - - return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/libgadu.h b/kopete/protocols/gadu/libgadu/libgadu.h deleted file mode 100644 index 85c54953..00000000 --- a/kopete/protocols/gadu/libgadu/libgadu.h +++ /dev/null @@ -1,1310 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo¼ny <speedy@ziew.org> - * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> - * Tomasz Chiliñski <chilek@chilan.com> - * Piotr Wysocki <wysek@linux.bydg.org> - * Dawid Jarosz <dawjar@poczta.onet.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#ifndef __GG_LIBGADU_H -#define __GG_LIBGADU_H - -#ifdef __cplusplus -#ifdef _WIN32 -#pragma pack(push, 1) -#endif -extern "C" { -#endif - -#include <libgadu-config.h> -#include <sys/types.h> -#include <stdio.h> -#include <stdarg.h> - -#ifdef __GG_LIBGADU_HAVE_OPENSSL -#include <openssl/ssl.h> -#endif - -/* - * typedef uin_t - * - * typ reprezentuj±cy numer osoby. - */ -typedef uint32_t uin_t; - -/* - * ogólna struktura opisuj±ca ró¿ne sesje. przydatna w klientach. - */ -#define gg_common_head(x) \ - int fd; /* podgl±dany deskryptor */ \ - int check; /* sprawdzamy zapis czy odczyt */ \ - int state; /* aktualny stan maszynki */ \ - int error; /* kod b³êdu dla GG_STATE_ERROR */ \ - int type; /* rodzaj sesji */ \ - int id; /* identyfikator */ \ - int timeout; /* sugerowany timeout w sekundach */ \ - int (*callback)(x*); /* callback przy zmianach */ \ - void (*destroy)(x*); /* funkcja niszczenia */ - -struct gg_common { - gg_common_head(struct gg_common) -}; - -struct gg_image_queue; - -/* - * struct gg_session - * - * struktura opisuj±ca dan± sesjê. tworzona przez gg_login(), zwalniana - * przez gg_free_session(). - */ -struct gg_session { - gg_common_head(struct gg_session) - - int async; /* czy po³±czenie jest asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z którym siê ³±czymy */ - int seq; /* numer sekwencyjny ostatniej wiadomo¶ci */ - int last_pong; /* czas otrzymania ostatniego ping/pong */ - int last_event; /* czas otrzymania ostatniego pakietu */ - - struct gg_event *event; /* zdarzenie po ->callback() */ - - uint32_t proxy_addr; /* adres proxy, keszowany */ - uint16_t proxy_port; /* port proxy */ - - uint32_t hub_addr; /* adres huba po resolvniêciu */ - uint32_t server_addr; /* adres serwera, od huba */ - - uint32_t client_addr; /* adres klienta */ - uint16_t client_port; /* port, na którym klient s³ucha */ - - uint32_t external_addr; /* adres zewnetrzny klienta */ - uint16_t external_port; /* port zewnetrzny klienta */ - - uin_t uin; /* numerek klienta */ - char *password; /* i jego has³o. zwalniane automagicznie */ - - int initial_status; /* pocz±tkowy stan klienta */ - int status; /* aktualny stan klienta */ - - char *recv_buf; /* bufor na otrzymywane pakiety */ - int recv_done; /* ile ju¿ wczytano do bufora */ - int recv_left; /* i ile jeszcze trzeba wczytaæ */ - - int protocol_version; /* wersja u¿ywanego protoko³u */ - char *client_version; /* wersja u¿ywanego klienta */ - int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */ - - char *initial_descr; /* pocz±tkowy opis stanu klienta */ - - void *resolver; /* wska¼nik na informacje resolvera */ - - char *header_buf; /* bufor na pocz±tek nag³ówka */ - unsigned int header_done;/* ile ju¿ mamy */ - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - SSL *ssl; /* sesja TLS */ - SSL_CTX *ssl_ctx; /* kontekst sesji? */ -#else - void *ssl; /* zachowujemy ABI */ - void *ssl_ctx; -#endif - - int image_size; /* maksymalny rozmiar obrazków w KiB */ - - char *userlist_reply; /* fragment odpowiedzi listy kontaktów */ - - int userlist_blocks; /* na ile kawa³ków podzielono listê kontaktów */ - - struct gg_image_queue *images; /* aktualnie wczytywane obrazki */ -}; - -/* - * struct gg_http - * - * ogólna struktura opisuj±ca stan wszystkich operacji HTTP. tworzona - * przez gg_http_connect(), zwalniana przez gg_http_free(). - */ -struct gg_http { - gg_common_head(struct gg_http) - - int async; /* czy po³±czenie asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z którym siê ³±czymy */ - - char *query; /* bufor zapytania http */ - char *header; /* bufor nag³ówka */ - int header_size; /* rozmiar wczytanego nag³ówka */ - char *body; /* bufor otrzymanych informacji */ - unsigned int body_size; /* oczekiwana ilo¶æ informacji */ - - void *data; /* dane danej operacji http */ - - char *user_data; /* dane u¿ytkownika, nie s± zwalniane przez gg_http_free() */ - - void *resolver; /* wska¼nik na informacje resolvera */ - - unsigned int body_done; /* ile ju¿ tre¶ci odebrano? */ -}; - -#ifdef __GNUC__ -#define GG_PACKED __attribute__ ((packed)) -#else -#define GG_PACKED -#endif - -#define GG_MAX_PATH 276 - -/* - * struct gg_file_info - * - * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbêdnej przy - * wysy³aniu plików. - */ -struct gg_file_info { - uint32_t mode; /* dwFileAttributes */ - uint32_t ctime[2]; /* ftCreationTime */ - uint32_t atime[2]; /* ftLastAccessTime */ - uint32_t mtime[2]; /* ftLastWriteTime */ - uint32_t size_hi; /* nFileSizeHigh */ - uint32_t size; /* nFileSizeLow */ - uint32_t reserved0; /* dwReserved0 */ - uint32_t reserved1; /* dwReserved1 */ - unsigned char filename[GG_MAX_PATH - 14]; /* cFileName */ - unsigned char short_filename[14]; /* cAlternateFileName */ -} GG_PACKED; - -/* - * struct gg_dcc - * - * struktura opisuj±ca nas³uchuj±ce gniazdo po³±czeñ miêdzy klientami. - * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free(). - */ -struct gg_dcc { - gg_common_head(struct gg_dcc) - - struct gg_event *event; /* opis zdarzenia */ - - int active; /* czy to my siê ³±czymy? */ - int port; /* port, na którym siedzi */ - uin_t uin; /* uin klienta */ - uin_t peer_uin; /* uin drugiej strony */ - int file_fd; /* deskryptor pliku */ - unsigned int offset; /* offset w pliku */ - unsigned int chunk_size;/* rozmiar kawa³ka */ - unsigned int chunk_offset;/* offset w aktualnym kawa³ku */ - struct gg_file_info file_info; - /* informacje o pliku */ - int established; /* po³±czenie ustanowione */ - char *voice_buf; /* bufor na pakiet po³±czenia g³osowego */ - int incoming; /* po³±czenie przychodz±ce */ - char *chunk_buf; /* bufor na kawa³ek danych */ - uint32_t remote_addr; /* adres drugiej strony */ - uint16_t remote_port; /* port drugiej strony */ -}; - -/* - * enum gg_session_t - * - * rodzaje sesji. - */ -enum gg_session_t { - GG_SESSION_GG = 1, /* po³±czenie z serwerem gg */ - GG_SESSION_HTTP, /* ogólna sesja http */ - GG_SESSION_SEARCH, /* szukanie */ - GG_SESSION_REGISTER, /* rejestrowanie */ - GG_SESSION_REMIND, /* przypominanie has³a */ - GG_SESSION_PASSWD, /* zmiana has³a */ - GG_SESSION_CHANGE, /* zmiana informacji o sobie */ - GG_SESSION_DCC, /* ogólne po³±czenie DCC */ - GG_SESSION_DCC_SOCKET, /* nas³uchuj±cy socket */ - GG_SESSION_DCC_SEND, /* wysy³anie pliku */ - GG_SESSION_DCC_GET, /* odbieranie pliku */ - GG_SESSION_DCC_VOICE, /* rozmowa g³osowa */ - GG_SESSION_USERLIST_GET, /* pobieranie userlisty */ - GG_SESSION_USERLIST_PUT, /* wysy³anie userlisty */ - GG_SESSION_UNREGISTER, /* usuwanie konta */ - GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */ - GG_SESSION_TOKEN, /* pobieranie tokenu */ - - GG_SESSION_USER0 = 256, /* zdefiniowana dla u¿ytkownika */ - GG_SESSION_USER1, /* j.w. */ - GG_SESSION_USER2, /* j.w. */ - GG_SESSION_USER3, /* j.w. */ - GG_SESSION_USER4, /* j.w. */ - GG_SESSION_USER5, /* j.w. */ - GG_SESSION_USER6, /* j.w. */ - GG_SESSION_USER7 /* j.w. */ -}; - -/* - * enum gg_state_t - * - * opisuje stan asynchronicznej maszyny. - */ -enum gg_state_t { - /* wspólne */ - GG_STATE_IDLE = 0, /* nie powinno wyst±piæ. */ - GG_STATE_RESOLVING, /* wywo³a³ gethostbyname() */ - GG_STATE_CONNECTING, /* wywo³a³ connect() */ - GG_STATE_READING_DATA, /* czeka na dane http */ - GG_STATE_ERROR, /* wyst±pi³ b³±d. kod w x->error */ - - /* gg_session */ - GG_STATE_CONNECTING_HUB, /* wywo³a³ connect() na huba */ - GG_STATE_CONNECTING_GG, /* wywo³a³ connect() na serwer */ - GG_STATE_READING_KEY, /* czeka na klucz */ - GG_STATE_READING_REPLY, /* czeka na odpowied¼ */ - GG_STATE_CONNECTED, /* po³±czy³ siê */ - - /* gg_http */ - GG_STATE_SENDING_QUERY, /* wysy³a zapytanie http */ - GG_STATE_READING_HEADER, /* czeka na nag³ówek http */ - GG_STATE_PARSING, /* przetwarza dane */ - GG_STATE_DONE, /* skoñczy³ */ - - /* gg_dcc */ - GG_STATE_LISTENING, /* czeka na po³±czenia */ - GG_STATE_READING_UIN_1, /* czeka na uin peera */ - GG_STATE_READING_UIN_2, /* czeka na swój uin */ - GG_STATE_SENDING_ACK, /* wysy³a potwierdzenie dcc */ - GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */ - GG_STATE_READING_REQUEST, /* czeka na komendê */ - GG_STATE_SENDING_REQUEST, /* wysy³a komendê */ - GG_STATE_SENDING_FILE_INFO, /* wysy³a informacje o pliku */ - GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */ - GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */ - GG_STATE_SENDING_FILE_ACK, /* wysy³a potwierdzenie pliku */ - GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */ - GG_STATE_SENDING_FILE_HEADER, /* wysy³a nag³ówek pliku */ - GG_STATE_READING_FILE_HEADER, /* czeka na nag³ówek */ - GG_STATE_GETTING_FILE, /* odbiera plik */ - GG_STATE_SENDING_FILE, /* wysy³a plik */ - GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */ - GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */ - GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */ - GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */ - GG_STATE_SENDING_VOICE_ACK, /* wysy³a potwierdzenie voip */ - GG_STATE_SENDING_VOICE_REQUEST, /* wysy³a ¿±danie voip */ - GG_STATE_READING_TYPE, /* czeka na typ po³±czenia */ - - /* nowe. bez sensu jest to API. */ - GG_STATE_TLS_NEGOTIATION /* negocjuje po³±czenie TLS */ -}; - -/* - * enum gg_check_t - * - * informuje, co proces klienta powinien sprawdziæ na deskryptorze danego - * po³±czenia. - */ -enum gg_check_t { - GG_CHECK_NONE = 0, /* nic. nie powinno wyst±piæ */ - GG_CHECK_WRITE = 1, /* sprawdzamy mo¿liwo¶æ zapisu */ - GG_CHECK_READ = 2 /* sprawdzamy mo¿liwo¶æ odczytu */ -}; - -/* - * struct gg_login_params - * - * parametry gg_login(). przeniesiono do struktury, ¿eby unikn±æ problemów - * z ci±g³ymi zmianami API, gdy dodano co¶ nowego do protoko³u. - */ -struct gg_login_params { - uin_t uin; /* numerek */ - char *password; /* has³o */ - int async; /* asynchroniczne sockety? */ - int status; /* pocz±tkowy status klienta */ - char *status_descr; /* opis statusu */ - uint32_t server_addr; /* adres serwera gg */ - uint16_t server_port; /* port serwera gg */ - uint32_t client_addr; /* adres dcc klienta */ - uint16_t client_port; /* port dcc klienta */ - int protocol_version; /* wersja protoko³u */ - char *client_version; /* wersja klienta */ - int has_audio; /* czy ma d¼wiêk? */ - int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */ - uint32_t external_addr; /* adres widziany na zewnatrz */ - uint16_t external_port; /* port widziany na zewnatrz */ - int tls; /* czy ³±czymy po TLS? */ - int image_size; /* maksymalny rozmiar obrazka w KiB */ - int era_omnix; /* czy udawaæ klienta era omnix? */ - - char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych, - * ¿eby z dodaniem parametru nie - * zmienia³ siê rozmiar struktury */ -}; - -struct gg_session *gg_login(const struct gg_login_params *p); -void gg_free_session(struct gg_session *sess); -void gg_logoff(struct gg_session *sess); -int gg_change_status(struct gg_session *sess, int status); -int gg_change_status_descr(struct gg_session *sess, int status, const char *descr); -int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time); -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message); -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen); -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message); -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen); -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len); -int gg_ping(struct gg_session *sess); -int gg_userlist_request(struct gg_session *sess, char type, const char *request); -int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32); -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); - -uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); - -struct gg_image_queue { - uin_t sender; /* nadawca obrazka */ - uint32_t size; /* rozmiar */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazem */ - uint32_t done; /* ile ju¿ wczytano */ - - struct gg_image_queue *next; /* nastêpny na li¶cie */ -}; - -/* - * enum gg_event_t - * - * rodzaje zdarzeñ. - */ -enum gg_event_t { - GG_EVENT_NONE = 0, /* nic siê nie wydarzy³o */ - GG_EVENT_MSG, /* otrzymano wiadomo¶æ */ - GG_EVENT_NOTIFY, /* kto¶ siê pojawi³ */ - GG_EVENT_NOTIFY_DESCR, /* kto¶ siê pojawi³ z opisem */ - GG_EVENT_STATUS, /* kto¶ zmieni³ stan */ - GG_EVENT_ACK, /* potwierdzenie wys³ania wiadomo¶ci */ - GG_EVENT_PONG, /* pakiet pong */ - GG_EVENT_CONN_FAILED, /* po³±czenie siê nie uda³o */ - GG_EVENT_CONN_SUCCESS, /* po³±czenie siê powiod³o */ - GG_EVENT_DISCONNECT, /* serwer zrywa po³±czenie */ - - GG_EVENT_DCC_NEW, /* nowe po³±czenie miêdzy klientami */ - GG_EVENT_DCC_ERROR, /* b³±d po³±czenia miêdzy klientami */ - GG_EVENT_DCC_DONE, /* zakoñczono po³±czenie */ - GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */ - GG_EVENT_DCC_CALLBACK, /* klient siê po³±czy³ na ¿±danie */ - GG_EVENT_DCC_NEED_FILE_INFO, /* nale¿y wype³niæ file_info */ - GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */ - GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */ - GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy g³osowej */ - - GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */ - GG_EVENT_PUBDIR50_READ, /* odczytano w³asne dane z katalogu */ - GG_EVENT_PUBDIR50_WRITE, /* wpisano w³asne dane do katalogu */ - - GG_EVENT_STATUS60, /* kto¶ zmieni³ stan w GG 6.0 */ - GG_EVENT_NOTIFY60, /* kto¶ siê pojawi³ w GG 6.0 */ - GG_EVENT_USERLIST, /* odpowied¼ listy kontaktów w GG 6.0 */ - GG_EVENT_IMAGE_REQUEST, /* pro¶ba o wys³anie obrazka GG 6.0 */ - GG_EVENT_IMAGE_REPLY, /* podes³any obrazek GG 6.0 */ - GG_EVENT_DCC_ACK /* potwierdzenie transmisji */ -}; - -#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY - -/* - * enum gg_failure_t - * - * okre¶la powód nieudanego po³±czenia. - */ -enum gg_failure_t { - GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */ - GG_FAILURE_CONNECTING, /* nie mo¿na siê po³±czyæ */ - GG_FAILURE_INVALID, /* serwer zwróci³ nieprawid³owe dane */ - GG_FAILURE_READING, /* zerwano po³±czenie podczas odczytu */ - GG_FAILURE_WRITING, /* zerwano po³±czenie podczas zapisu */ - GG_FAILURE_PASSWORD, /* nieprawid³owe has³o */ - GG_FAILURE_404, /* XXX nieu¿ywane */ - GG_FAILURE_TLS, /* b³±d negocjacji TLS */ - GG_FAILURE_NEED_EMAIL, /* serwer roz³±czy³ nas z pro¶b± o zmianê emaila */ - GG_FAILURE_INTRUDER, /* za du¿o prób po³±czenia siê z nieprawid³owym has³em */ - GG_FAILURE_UNAVAILABLE /* serwery s± wy³±czone */ -}; - -/* - * enum gg_error_t - * - * okre¶la rodzaj b³êdu wywo³anego przez dan± operacjê. nie zawiera - * przesadnie szczegó³owych informacji o powodzie b³êdu, by nie komplikowaæ - * obs³ugi b³êdów. je¶li wymagana jest wiêksza dok³adno¶æ, nale¿y sprawdziæ - * zawarto¶æ zmiennej errno. - */ -enum gg_error_t { - GG_ERROR_RESOLVING = 1, /* b³±d znajdowania hosta */ - GG_ERROR_CONNECTING, /* b³±d ³aczenia siê */ - GG_ERROR_READING, /* b³±d odczytu */ - GG_ERROR_WRITING, /* b³±d wysy³ania */ - - GG_ERROR_DCC_HANDSHAKE, /* b³±d negocjacji */ - GG_ERROR_DCC_FILE, /* b³±d odczytu/zapisu pliku */ - GG_ERROR_DCC_EOF, /* plik siê skoñczy³? */ - GG_ERROR_DCC_NET, /* b³±d wysy³ania/odbierania */ - GG_ERROR_DCC_REFUSED /* po³±czenie odrzucone przez usera */ -}; - -/* - * struktury dotycz±ce wyszukiwania w GG 5.0. NIE NALE¯Y SIÊ DO NICH - * ODWO£YWAÆ BEZPO¦REDNIO! do dostêpu do nich s³u¿± funkcje gg_pubdir50_*() - */ -struct gg_pubdir50_entry { - int num; - char *field; - char *value; -}; - -struct gg_pubdir50_s { - int count; - uin_t next; - int type; - uint32_t seq; - struct gg_pubdir50_entry *entries; - int entries_count; -}; - -/* - * typedef gg_pubdir_50_t - * - * typ opisuj±cy zapytanie lub wynik zapytania katalogu publicznego - * z protoko³u GG 5.0. nie nale¿y siê odwo³ywaæ bezpo¶rednio do jego - * pól -- s³u¿± do tego funkcje gg_pubdir50_*() - */ -typedef struct gg_pubdir50_s *gg_pubdir50_t; - -/* - * struct gg_event - * - * struktura opisuj±ca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub - * z gg_dcc_watch_fd() - */ -struct gg_event { - int type; /* rodzaj zdarzenia -- gg_event_t */ - union { /* @event */ - struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów -- GG_EVENT_NOTIFY */ - - enum gg_failure_t failure; /* b³±d po³±czenia -- GG_EVENT_FAILURE */ - - struct gg_dcc *dcc_new; /* nowe po³±czenie bezpo¶rednie -- GG_EVENT_DCC_NEW */ - - int dcc_error; /* b³±d po³±czenia bezpo¶redniego -- GG_EVENT_DCC_ERROR */ - - gg_pubdir50_t pubdir50; /* wynik operacji zwi±zanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */ - - struct { /* @msg odebrano wiadomo¶æ -- GG_EVENT_MSG */ - uin_t sender; /* numer nadawcy */ - int msgclass; /* klasa wiadomo¶ci */ - time_t time; /* czas nadania */ - unsigned char *message; /* tre¶æ wiadomo¶ci */ - - int recipients_count; /* ilo¶æ odbiorców konferencji */ - uin_t *recipients; /* odbiorcy konferencji */ - - int formats_length; /* d³ugo¶æ informacji o formatowaniu tekstu */ - void *formats; /* informacje o formatowaniu tekstu */ - } msg; - - struct { /* @notify_descr informacje o li¶cie kontaktów z opisami stanu -- GG_EVENT_NOTIFY_DESCR */ - struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów */ - char *descr; /* opis stanu */ - } notify_descr; - - struct { /* @status zmiana stanu -- GG_EVENT_STATUS */ - uin_t uin; /* numer */ - uint32_t status; /* nowy stan */ - char *descr; /* opis stanu */ - } status; - - struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ - uin_t uin; /* numer */ - int status; /* nowy stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } status60; - - struct { /* @notify60 informacja o li¶cie kontaktów -- GG_EVENT_NOTIFY60 */ - uin_t uin; /* numer */ - int status; /* stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } *notify60; - - struct { /* @ack potwierdzenie wiadomo¶ci -- GG_EVENT_ACK */ - uin_t recipient; /* numer odbiorcy */ - int status; /* stan dorêczenia wiadomo¶ci */ - int seq; /* numer sekwencyjny wiadomo¶ci */ - } ack; - - struct { /* @dcc_voice_data otrzymano dane d¼wiêkowe -- GG_EVENT_DCC_VOICE_DATA */ - uint8_t *data; /* dane d¼wiêkowe */ - int length; /* ilo¶æ danych d¼wiêkowych */ - } dcc_voice_data; - - struct { /* @userlist odpowied¼ listy kontaktów serwera */ - char type; /* rodzaj odpowiedzi */ - char *reply; /* tre¶æ odpowiedzi */ - } userlist; - - struct { /* @image_request pro¶ba o obrazek */ - uin_t sender; /* nadawca pro¶by */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - } image_request; - - struct { /* @image_reply odpowied¼ z obrazkiem */ - uin_t sender; /* nadawca odpowiedzi */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazkiem */ - } image_reply; - } event; -}; - -struct gg_event *gg_watch_fd(struct gg_session *sess); -void gg_event_free(struct gg_event *e); -#define gg_free_event gg_event_free - -/* - * funkcje obs³ugi listy kontaktów. - */ -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count); -int gg_notify(struct gg_session *sess, uin_t *userlist, int count); -int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type); -int gg_add_notify(struct gg_session *sess, uin_t uin); -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type); -int gg_remove_notify(struct gg_session *sess, uin_t uin); - -/* - * funkcje obs³ugi http. - */ -struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header); -int gg_http_watch_fd(struct gg_http *h); -void gg_http_stop(struct gg_http *h); -void gg_http_free(struct gg_http *h); -void gg_http_free_fields(struct gg_http *h); -#define gg_free_http gg_http_free - -/* - * struktury opisuj±ca kryteria wyszukiwania dla gg_search(). nieaktualne, - * zast±pione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI. - */ -struct gg_search_request { - int active; - unsigned int start; - char *nickname; - char *first_name; - char *last_name; - char *city; - int gender; - int min_birth; - int max_birth; - char *email; - char *phone; - uin_t uin; -}; - -struct gg_search { - int count; - struct gg_search_result *results; -}; - -struct gg_search_result { - uin_t uin; - char *first_name; - char *last_name; - char *nickname; - int born; - int gender; - char *city; - int active; -}; - -#define GG_GENDER_NONE 0 -#define GG_GENDER_FEMALE 1 -#define GG_GENDER_MALE 2 - -/* - * funkcje wyszukiwania. - */ -struct gg_http *gg_search(const struct gg_search_request *r, int async); -int gg_search_watch_fd(struct gg_http *f); -void gg_free_search(struct gg_http *f); -#define gg_search_free gg_free_search - -const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start); -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start); -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start); -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start); -void gg_search_request_free(struct gg_search_request *r); - -/* - * funkcje obs³ugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje - * zachowuj± pewien poziom abstrakcji, ¿eby unikn±æ zmian ABI przy zmianach - * w protokole. - * - * NIE NALE¯Y SIÊ ODWO£YWAÆ DO PÓL gg_pubdir50_t BEZPO¦REDNIO! - */ -uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req); -gg_pubdir50_t gg_pubdir50_new(int type); -int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value); -int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq); -const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field); -int gg_pubdir50_type(gg_pubdir50_t res); -int gg_pubdir50_count(gg_pubdir50_t res); -uin_t gg_pubdir50_next(gg_pubdir50_t res); -uint32_t gg_pubdir50_seq(gg_pubdir50_t res); -void gg_pubdir50_free(gg_pubdir50_t res); - -#define GG_PUBDIR50_UIN "FmNumber" -#define GG_PUBDIR50_STATUS "FmStatus" -#define GG_PUBDIR50_FIRSTNAME "firstname" -#define GG_PUBDIR50_LASTNAME "lastname" -#define GG_PUBDIR50_NICKNAME "nickname" -#define GG_PUBDIR50_BIRTHYEAR "birthyear" -#define GG_PUBDIR50_CITY "city" -#define GG_PUBDIR50_GENDER "gender" -#define GG_PUBDIR50_GENDER_FEMALE "1" -#define GG_PUBDIR50_GENDER_MALE "2" -#define GG_PUBDIR50_GENDER_SET_FEMALE "2" -#define GG_PUBDIR50_GENDER_SET_MALE "1" -#define GG_PUBDIR50_ACTIVE "ActiveOnly" -#define GG_PUBDIR50_ACTIVE_TRUE "1" -#define GG_PUBDIR50_START "fmstart" -#define GG_PUBDIR50_FAMILYNAME "familyname" -#define GG_PUBDIR50_FAMILYCITY "familycity" - -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length); - -/* - * struct gg_pubdir - * - * operacje na katalogu publicznym. - */ -struct gg_pubdir { - int success; /* czy siê uda³o */ - uin_t uin; /* otrzymany numerek. 0 je¶li b³±d */ -}; - -/* ogólne funkcje, nie powinny byæ u¿ywane */ -int gg_pubdir_watch_fd(struct gg_http *f); -void gg_pubdir_free(struct gg_http *f); -#define gg_free_pubdir gg_pubdir_free - -struct gg_token { - int width; /* szeroko¶æ obrazka */ - int height; /* wysoko¶æ obrazka */ - int length; /* ilo¶æ znaków w tokenie */ - char *tokenid; /* id tokenu */ -}; - -/* funkcje dotycz±ce tokenów */ -struct gg_http *gg_token(int async); -int gg_token_watch_fd(struct gg_http *h); -void gg_token_free(struct gg_http *h); - -/* rejestracja nowego numerka */ -struct gg_http *gg_register(const char *email, const char *password, int async); -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async); -struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async); -#define gg_register_watch_fd gg_pubdir_watch_fd -#define gg_register_free gg_pubdir_free -#define gg_free_register gg_pubdir_free - -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async); -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async); -struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async); -#define gg_unregister_watch_fd gg_pubdir_watch_fd -#define gg_unregister_free gg_pubdir_free - -/* przypomnienie has³a e-mailem */ -struct gg_http *gg_remind_passwd(uin_t uin, int async); -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async); -struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async); -#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd -#define gg_remind_passwd_free gg_pubdir_free -#define gg_free_remind_passwd gg_pubdir_free - -/* zmiana has³a */ -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async); -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async); -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async); -struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async); -#define gg_change_passwd_free gg_pubdir_free -#define gg_free_change_passwd gg_pubdir_free - -/* - * struct gg_change_info_request - * - * opis ¿±dania zmiany informacji w katalogu publicznym. - */ -struct gg_change_info_request { - char *first_name; /* imiê */ - char *last_name; /* nazwisko */ - char *nickname; /* pseudonim */ - char *email; /* email */ - int born; /* rok urodzenia */ - int gender; /* p³eæ */ - char *city; /* miasto */ -}; - -struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city); -void gg_change_info_request_free(struct gg_change_info_request *r); - -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async); -#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd -#define gg_change_pubdir_free gg_pubdir_free -#define gg_free_change_pubdir gg_pubdir_free - -/* - * funkcje dotycz±ce listy kontaktów na serwerze. - */ -struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async); -int gg_userlist_get_watch_fd(struct gg_http *f); -void gg_userlist_get_free(struct gg_http *f); - -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async); -int gg_userlist_put_watch_fd(struct gg_http *f); -void gg_userlist_put_free(struct gg_http *f); - -struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async); -int gg_userlist_remove_watch_fd(struct gg_http *f); -void gg_userlist_remove_free(struct gg_http *f); - - - -/* - * funkcje dotycz±ce komunikacji miêdzy klientami. - */ -extern int gg_dcc_port; /* port, na którym nas³uchuje klient */ -extern unsigned long gg_dcc_ip; /* adres, na którym nas³uchuje klient */ - -int gg_dcc_request(struct gg_session *sess, uin_t uin); - -struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -void gg_dcc_set_type(struct gg_dcc *d, int type); -int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename); -int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename); -int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length); - -#define GG_DCC_VOICE_FRAME_LENGTH 195 -#define GG_DCC_VOICE_FRAME_LENGTH_505 326 - -struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port); -#define gg_dcc_socket_free gg_free_dcc -#define gg_dcc_socket_watch_fd gg_dcc_watch_fd - -struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d); - -void gg_dcc_free(struct gg_dcc *c); -#define gg_free_dcc gg_dcc_free - -/* - * je¶li chcemy sobie podebugowaæ, wystarczy ustawiæ `gg_debug_level'. - * niestety w miarê przybywania wpisów `gg_debug(...)' nie chcia³o mi - * siê ustawiaæ odpowiednich leveli, wiêc wiêkszo¶æ sz³a do _MISC. - */ -extern int gg_debug_level; /* poziom debugowania. mapa bitowa sta³ych GG_DEBUG_* */ - -/* - * mo¿na podaæ wska¼nik do funkcji obs³uguj±cej wywo³ania gg_debug(). - * nieoficjalne, nieudokumentowane, mo¿e siê zmieniæ. je¶li kto¶ jest - * zainteresowany, niech da znaæ na ekg-devel. - */ -extern void (*gg_debug_handler)(int level, const char *format, va_list ap); - -/* - * mo¿na podaæ plik, do którego bêd± zapisywane teksty z gg_debug(). - */ -extern FILE *gg_debug_file; - -#define GG_DEBUG_NET 1 -#define GG_DEBUG_TRAFFIC 2 -#define GG_DEBUG_DUMP 4 -#define GG_DEBUG_FUNCTION 8 -#define GG_DEBUG_MISC 16 - -#ifdef GG_DEBUG_DISABLE -#define gg_debug(x, y...) do { } while(0) -#else -void gg_debug(int level, const char *format, ...); -#endif - -const char *gg_libgadu_version(void); - -/* - * konfiguracja http proxy. - */ -extern int gg_proxy_enabled; /* w³±cza obs³ugê proxy */ -extern char *gg_proxy_host; /* okre¶la adres serwera proxy */ -extern int gg_proxy_port; /* okre¶la port serwera proxy */ -extern char *gg_proxy_username; /* okre¶la nazwê u¿ytkownika przy autoryzacji serwera proxy */ -extern char *gg_proxy_password; /* okre¶la has³o u¿ytkownika przy autoryzacji serwera proxy */ -extern int gg_proxy_http_only; /* w³±cza obs³ugê proxy wy³±cznie dla us³ug HTTP */ - - -/* - * adres, z którego ¶lemy pakiety (np ³±czymy siê z serwerem) - * u¿ywany przy gg_connect() - */ -extern unsigned long gg_local_ip; -/* - * ------------------------------------------------------------------------- - * poni¿ej znajduj± siê wewnêtrzne sprawy biblioteki. zwyk³y klient nie - * powinien ich w ogóle ruszaæ, bo i nie ma po co. wszystko mo¿na za³atwiæ - * procedurami wy¿szego poziomu, których definicje znajduj± siê na pocz±tku - * tego pliku. - * ------------------------------------------------------------------------- - */ - -#ifdef __GG_LIBGADU_HAVE_PTHREAD -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); -#endif - -#ifdef _WIN32 -int gg_thread_socket(int thread_id, int socket); -#endif - -int gg_resolve(int *fd, int *pid, const char *hostname); - -#ifdef __GNUC__ -char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -#else -char *gg_saprintf(const char *format, ...); -#endif - -char *gg_vsaprintf(const char *format, va_list ap); - -#define gg_alloc_sprintf gg_saprintf - -char *gg_get_line(char **ptr); - -int gg_connect(void *addr, int port, int async); -struct in_addr *gg_gethostbyname(const char *hostname); -char *gg_read_line(int sock, char *buf, int length); -void gg_chomp(char *line); -char *gg_urlencode(const char *str); -int gg_http_hash(const char *format, ...); -int gg_read(struct gg_session *sess, char *buf, int length); -int gg_write(struct gg_session *sess, const char *buf, int length); -void *gg_recv_packet(struct gg_session *sess); -int gg_send_packet(struct gg_session *sess, int type, ...); -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed); -uint32_t gg_fix32(uint32_t x); -uint16_t gg_fix16(uint16_t x); -#define fix16 gg_fix16 -#define fix32 gg_fix32 -char *gg_proxy_auth(void); -char *gg_base64_encode(const char *buf); -char *gg_base64_decode(const char *buf); -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq); - -#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" -#define GG_APPMSG_PORT 80 -#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl" -#define GG_PUBDIR_PORT 80 -#define GG_REGISTER_HOST "register.gadu-gadu.pl" -#define GG_REGISTER_PORT 80 -#define GG_REMIND_HOST "retr.gadu-gadu.pl" -#define GG_REMIND_PORT 80 - -#define GG_DEFAULT_PORT 8074 -#define GG_HTTPS_PORT 443 -#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)" - -#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158" -#define GG_DEFAULT_PROTOCOL_VERSION 0x24 -#define GG_DEFAULT_TIMEOUT 30 -#define GG_HAS_AUDIO_MASK 0x40000000 -#define GG_ERA_OMNIX_MASK 0x04000000 -#define GG_LIBGADU_VERSION "CVS" - -#define GG_DEFAULT_DCC_PORT 1550 - -struct gg_header { - uint32_t type; /* typ pakietu */ - uint32_t length; /* d³ugo¶æ reszty pakietu */ -} GG_PACKED; - -#define GG_WELCOME 0x0001 -#define GG_NEED_EMAIL 0x0014 - -struct gg_welcome { - uint32_t key; /* klucz szyfrowania has³a */ -} GG_PACKED; - -#define GG_LOGIN 0x000c - -struct gg_login { - uint32_t uin; /* mój numerek */ - uint32_t hash; /* hash has³a */ - uint32_t status; /* status na dzieñ dobry */ - uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mój adres ip */ - uint16_t local_port; /* port, na którym s³ucham */ -} GG_PACKED; - -#define GG_LOGIN_EXT 0x0013 - -struct gg_login_ext { - uint32_t uin; /* mój numerek */ - uint32_t hash; /* hash has³a */ - uint32_t status; /* status na dzieñ dobry */ - uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mój adres ip */ - uint16_t local_port; /* port, na którym s³ucham */ - uint32_t external_ip; /* zewnêtrzny adres ip */ - uint16_t external_port; /* zewnêtrzny port */ -} GG_PACKED; - -#define GG_LOGIN60 0x0015 - -struct gg_login60 { - uint32_t uin; /* mój numerek */ - uint32_t hash; /* hash has³a */ - uint32_t status; /* status na dzieñ dobry */ - uint32_t version; /* moja wersja klienta */ - uint8_t dunno1; /* 0x00 */ - uint32_t local_ip; /* mój adres ip */ - uint16_t local_port; /* port, na którym s³ucham */ - uint32_t external_ip; /* zewnêtrzny adres ip */ - uint16_t external_port; /* zewnêtrzny port */ - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ - uint8_t dunno2; /* 0xbe */ -} GG_PACKED; - -#define GG_LOGIN_OK 0x0003 - -#define GG_LOGIN_FAILED 0x0009 - -#define GG_PUBDIR50_REQUEST 0x0014 - -#define GG_PUBDIR50_WRITE 0x01 -#define GG_PUBDIR50_READ 0x02 -#define GG_PUBDIR50_SEARCH 0x03 -#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH -#define GG_PUBDIR50_SEARCH_REPLY 0x05 - -struct gg_pubdir50_request { - uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wys³ania zapytania */ -} GG_PACKED; - -#define GG_PUBDIR50_REPLY 0x000e - -struct gg_pubdir50_reply { - uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wys³ania zapytania */ -} GG_PACKED; - -#define GG_NEW_STATUS 0x0002 - -#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostêpny */ -#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostêpny z opisem (4.8) */ -#define GG_STATUS_AVAIL 0x0002 /* dostêpny */ -#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostêpny z opisem (4.9) */ -#define GG_STATUS_BUSY 0x0003 /* zajêty */ -#define GG_STATUS_BUSY_DESCR 0x0005 /* zajêty z opisem (4.8) */ -#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ -#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ -#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ - -#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ - -#define GG_STATUS_DESCR_MAXSIZE 70 - -/* - * makra do ³atwego i szybkiego sprawdzania stanu. - */ - -/* GG_S_F() tryb tylko dla znajomych */ -#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0) - -/* GG_S() stan bez uwzglêdnienia trybu tylko dla znajomych */ -#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) - -/* GG_S_A() dostêpny */ -#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) - -/* GG_S_NA() niedostêpny */ -#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) - -/* GG_S_B() zajêty */ -#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR) - -/* GG_S_I() niewidoczny */ -#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) - -/* GG_S_D() stan opisowy */ -#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) - -/* GG_S_BL() blokowany lub blokuj±cy */ -#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED) - -struct gg_new_status { - uint32_t status; /* na jaki zmieniæ? */ -} GG_PACKED; - -#define GG_NOTIFY_FIRST 0x000f -#define GG_NOTIFY_LAST 0x0010 - -#define GG_NOTIFY 0x0010 - -struct gg_notify { - uint32_t uin; /* numerek danej osoby */ - uint8_t dunno1; /* rodzaj wpisu w li¶cie */ -} GG_PACKED; - -#define GG_USER_OFFLINE 0x01 /* bêdziemy niewidoczni dla u¿ytkownika */ -#define GG_USER_NORMAL 0x03 /* zwyk³y u¿ytkownik */ -#define GG_USER_BLOCKED 0x04 /* zablokowany u¿ytkownik */ - -#define GG_LIST_EMPTY 0x0012 - -#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */ - -struct gg_notify_reply { - uint32_t uin; /* numerek */ - uint32_t status; /* status danej osoby */ - uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na którym s³ucha klient */ - uint32_t version; /* wersja klienta */ - uint16_t dunno2; /* znowu port? */ -} GG_PACKED; - -#define GG_NOTIFY_REPLY60 0x0011 - -struct gg_notify_reply60 { - uint32_t uin; /* numerek plus flagi w MSB */ - uint8_t status; /* status danej osoby */ - uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na którym s³ucha klient */ - uint8_t version; /* wersja klienta */ - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ - uint8_t dunno1; /* 0x00 */ -} GG_PACKED; - -#define GG_STATUS60 0x000f - -struct gg_status60 { - uint32_t uin; /* numerek plus flagi w MSB */ - uint8_t status; /* status danej osoby */ - uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na którym s³ucha klient */ - uint8_t version; /* wersja klienta */ - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ - uint8_t dunno1; /* 0x00 */ -} GG_PACKED; - -#define GG_ADD_NOTIFY 0x000d -#define GG_REMOVE_NOTIFY 0x000e - -struct gg_add_remove { - uint32_t uin; /* numerek */ - uint8_t dunno1; /* bitmapa */ -} GG_PACKED; - -#define GG_STATUS 0x0002 - -struct gg_status { - uint32_t uin; /* numerek */ - uint32_t status; /* nowy stan */ -} GG_PACKED; - -#define GG_SEND_MSG 0x000b - -#define GG_CLASS_QUEUED 0x0001 -#define GG_CLASS_OFFLINE GG_CLASS_QUEUED -#define GG_CLASS_MSG 0x0004 -#define GG_CLASS_CHAT 0x0008 -#define GG_CLASS_CTCP 0x0010 -#define GG_CLASS_ACK 0x0020 -#define GG_CLASS_EXT GG_CLASS_ACK /* kompatybilno¶æ wstecz */ - -#define GG_MSG_MAXSIZE 2000 - -struct gg_send_msg { - uint32_t recipient; - uint32_t seq; - uint32_t msgclass; -} GG_PACKED; - -struct gg_msg_richtext { - uint8_t flag; - uint16_t length; -} GG_PACKED; - -struct gg_msg_richtext_format { - uint16_t position; - uint8_t font; -} GG_PACKED; - -struct gg_msg_richtext_image { - uint16_t unknown1; - uint32_t size; - uint32_t crc32; -} GG_PACKED; - -#define GG_FONT_BOLD 0x01 -#define GG_FONT_ITALIC 0x02 -#define GG_FONT_UNDERLINE 0x04 -#define GG_FONT_COLOR 0x08 -#define GG_FONT_IMAGE 0x80 - -struct gg_msg_richtext_color { - uint8_t red; - uint8_t green; - uint8_t blue; -} GG_PACKED; - -struct gg_msg_recipients { - uint8_t flag; - uint32_t count; -} GG_PACKED; - -struct gg_msg_image_request { - uint8_t flag; - uint32_t size; - uint32_t crc32; -} GG_PACKED; - -struct gg_msg_image_reply { - uint8_t flag; - uint32_t size; - uint32_t crc32; - /* char filename[]; */ - /* char image[]; */ -} GG_PACKED; - -#define GG_SEND_MSG_ACK 0x0005 - -#define GG_ACK_BLOCKED 0x0001 -#define GG_ACK_DELIVERED 0x0002 -#define GG_ACK_QUEUED 0x0003 -#define GG_ACK_MBOXFULL 0x0004 -#define GG_ACK_NOT_DELIVERED 0x0006 - -struct gg_send_msg_ack { - uint32_t status; - uint32_t recipient; - uint32_t seq; -} GG_PACKED; - -#define GG_RECV_MSG 0x000a - -struct gg_recv_msg { - uint32_t sender; - uint32_t seq; - uint32_t time; - uint32_t msgclass; -} GG_PACKED; - -#define GG_PING 0x0008 - -#define GG_PONG 0x0007 - -#define GG_DISCONNECTING 0x000b - -#define GG_USERLIST_REQUEST 0x0016 - -#define GG_USERLIST_PUT 0x00 -#define GG_USERLIST_PUT_MORE 0x01 -#define GG_USERLIST_GET 0x02 - -struct gg_userlist_request { - uint8_t type; -} GG_PACKED; - -#define GG_USERLIST_REPLY 0x0010 - -#define GG_USERLIST_PUT_REPLY 0x00 -#define GG_USERLIST_PUT_MORE_REPLY 0x02 -#define GG_USERLIST_GET_REPLY 0x06 -#define GG_USERLIST_GET_MORE_REPLY 0x04 - -struct gg_userlist_reply { - uint8_t type; -} GG_PACKED; - -/* - * pakiety, sta³e, struktury dla DCC - */ - -struct gg_dcc_tiny_packet { - uint8_t type; /* rodzaj pakietu */ -} GG_PACKED; - -struct gg_dcc_small_packet { - uint32_t type; /* rodzaj pakietu */ -} GG_PACKED; - -struct gg_dcc_big_packet { - uint32_t type; /* rodzaj pakietu */ - uint32_t dunno1; /* niewiadoma */ - uint32_t dunno2; /* niewiadoma */ -} GG_PACKED; - -/* - * póki co, nie znamy dok³adnie protoko³u. nie wiemy, co czemu odpowiada. - * nazwy s± niepowa¿ne i tymczasowe. - */ -#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */ -#define GG_DCC_HAVE_FILE 0x0001 /* wiêc mu damy */ -#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */ -#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */ -#define GG_DCC_CATCH_FILE 0x0002 /* wysy³amy plik */ - -#define GG_DCC_FILEATTR_READONLY 0x0020 - -#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */ -#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */ -#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */ -#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */ - -#ifdef __cplusplus -} -#ifdef _WIN32 -#pragma pack(pop) -#endif -#endif - -#endif /* __GG_LIBGADU_H */ - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/pubdir.c b/kopete/protocols/gadu/libgadu/pubdir.c deleted file mode 100644 index 2741ea4b..00000000 --- a/kopete/protocols/gadu/libgadu/pubdir.c +++ /dev/null @@ -1,689 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> - * Dawid Jarosz <dawjar@poczta.onet.pl> - * Adam Wysocki <gophi@ekg.chmurka.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <ctype.h> -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "libgadu.h" - -/* - * gg_register3() - * - * rozpoczyna rejestracj� u�ytkownika protoko�em GG 6.0. wymaga wcze�niejszego - * pobrania tokenu za pomoc� funkcji gg_token(). - * - * - email - adres e-mail klienta - * - password - has�o klienta - * - tokenid - identyfikator tokenu - * - tokenval - warto�� tokenu - * - async - po��czenie asynchroniczne - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y zwolni� - * funkcj� gg_register_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async) -{ - struct gg_http *h; - char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query; - - if (!email || !password || !tokenid || !tokenval) { - gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n"); - errno = EFAULT; - return NULL; - } - - __pwd = gg_urlencode(password); - __email = gg_urlencode(email); - __tokenid = gg_urlencode(tokenid); - __tokenval = gg_urlencode(tokenval); - - if (!__pwd || !__email || !__tokenid || !__tokenval) { - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n"); - free(__pwd); - free(__email); - free(__tokenid); - free(__tokenval); - return NULL; - } - - form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", - __pwd, __email, __tokenid, __tokenval, - gg_http_hash("ss", email, password)); - - free(__pwd); - free(__email); - free(__tokenid); - free(__tokenval); - - if (!form) { - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n"); - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form); - - query = gg_saprintf( - "Host: " GG_REGISTER_HOST "\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Content-Length: %d\r\n" - "Pragma: no-cache\r\n" - "\r\n" - "%s", - (int) strlen(form), form); - - free(form); - - if (!query) { - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n"); - return NULL; - } - - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { - gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n"); - free(query); - return NULL; - } - - h->type = GG_SESSION_REGISTER; - - free(query); - - h->callback = gg_pubdir_watch_fd; - h->destroy = gg_pubdir_free; - - if (!async) - gg_pubdir_watch_fd(h); - - return h; -} - -/* - * gg_unregister3() - * - * usuwa konto u�ytkownika z serwera protoko�em GG 6.0 - * - * - uin - numerek GG - * - password - has�o klienta - * - tokenid - identyfikator tokenu - * - tokenval - warto�� tokenu - * - async - po��czenie asynchroniczne - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y zwolni� - * funkcj� gg_unregister_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async) -{ - struct gg_http *h; - char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query; - - if (!password || !tokenid || !tokenval) { - gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n"); - errno = EFAULT; - return NULL; - } - - __pwd = gg_saprintf("%ld", random()); - __fmpwd = gg_urlencode(password); - __tokenid = gg_urlencode(tokenid); - __tokenval = gg_urlencode(tokenval); - - if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) { - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n"); - free(__pwd); - free(__fmpwd); - free(__tokenid); - free(__tokenval); - return NULL; - } - - form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval, gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd)); - - free(__fmpwd); - free(__pwd); - free(__tokenid); - free(__tokenval); - - if (!form) { - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n"); - return NULL; - } - - gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form); - - query = gg_saprintf( - "Host: " GG_REGISTER_HOST "\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Content-Length: %d\r\n" - "Pragma: no-cache\r\n" - "\r\n" - "%s", - (int) strlen(form), form); - - free(form); - - if (!query) { - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n"); - return NULL; - } - - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { - gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n"); - free(query); - return NULL; - } - - h->type = GG_SESSION_UNREGISTER; - - free(query); - - h->callback = gg_pubdir_watch_fd; - h->destroy = gg_pubdir_free; - - if (!async) - gg_pubdir_watch_fd(h); - - return h; -} - -/* - * gg_change_passwd4() - * - * wysy�a ��danie zmiany has�a zgodnie z protoko�em GG 6.0. wymaga - * wcze�niejszego pobrania tokenu za pomoc� funkcji gg_token(). - * - * - uin - numer - * - email - adres e-mail - * - passwd - stare has�o - * - newpasswd - nowe has�o - * - tokenid - identyfikator tokenu - * - tokenval - warto�� tokenu - * - async - po��czenie asynchroniczne - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y zwolni� - * funkcj� gg_change_passwd_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async) -{ - struct gg_http *h; - char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval; - - if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) { - gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n"); - errno = EFAULT; - return NULL; - } - - __fmpwd = gg_urlencode(passwd); - __pwd = gg_urlencode(newpasswd); - __email = gg_urlencode(email); - __tokenid = gg_urlencode(tokenid); - __tokenval = gg_urlencode(tokenval); - - if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) { - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n"); - free(__fmpwd); - free(__pwd); - free(__email); - free(__tokenid); - free(__tokenval); - return NULL; - } - - if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd)))) { - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n"); - free(__fmpwd); - free(__pwd); - free(__email); - free(__tokenid); - free(__tokenval); - - return NULL; - } - - free(__fmpwd); - free(__pwd); - free(__email); - free(__tokenid); - free(__tokenval); - - gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form); - - query = gg_saprintf( - "Host: " GG_REGISTER_HOST "\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Content-Length: %d\r\n" - "Pragma: no-cache\r\n" - "\r\n" - "%s", - (int) strlen(form), form); - - free(form); - - if (!query) { - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n"); - return NULL; - } - - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { - gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n"); - free(query); - return NULL; - } - - h->type = GG_SESSION_PASSWD; - - free(query); - - h->callback = gg_pubdir_watch_fd; - h->destroy = gg_pubdir_free; - - if (!async) - gg_pubdir_watch_fd(h); - - return h; -} - -/* - * gg_remind_passwd3() - * - * wysy�a ��danie przypomnienia has�a e-mailem. - * - * - uin - numer - * - email - adres e-mail taki, jak ten zapisany na serwerze - * - async - po��czenie asynchroniczne - * - tokenid - identyfikator tokenu - * - tokenval - warto�� tokenu - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y zwolni� - * funkcj� gg_remind_passwd_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async) -{ - struct gg_http *h; - char *form, *query, *__tokenid, *__tokenval, *__email; - - if (!tokenid || !tokenval || !email) { - gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n"); - errno = EFAULT; - return NULL; - } - - __tokenid = gg_urlencode(tokenid); - __tokenval = gg_urlencode(tokenval); - __email = gg_urlencode(email); - - if (!__tokenid || !__tokenval || !__email) { - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); - free(__tokenid); - free(__tokenval); - free(__email); - return NULL; - } - - if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) { - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); - free(__tokenid); - free(__tokenval); - free(__email); - return NULL; - } - - free(__tokenid); - free(__tokenval); - free(__email); - - gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form); - - query = gg_saprintf( - "Host: " GG_REMIND_HOST "\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Content-Length: %d\r\n" - "Pragma: no-cache\r\n" - "\r\n" - "%s", - (int) strlen(form), form); - - free(form); - - if (!query) { - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n"); - return NULL; - } - - if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) { - gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n"); - free(query); - return NULL; - } - - h->type = GG_SESSION_REMIND; - - free(query); - - h->callback = gg_pubdir_watch_fd; - h->destroy = gg_pubdir_free; - - if (!async) - gg_pubdir_watch_fd(h); - - return h; -} - -/* - * gg_pubdir_watch_fd() - * - * przy asynchronicznych operacjach na katalogu publicznym nale�y wywo�ywa� - * t� funkcj� przy zmianach na obserwowanym deskryptorze. - * - * - h - struktura opisuj�ca po��czenie - * - * je�li wszystko posz�o dobrze to 0, inaczej -1. operacja b�dzie - * zako�czona, je�li h->state == GG_STATE_DONE. je�li wyst�pi jaki� - * b��d, to b�dzie tam GG_STATE_ERROR i odpowiedni kod b��du w h->error. - */ -int gg_pubdir_watch_fd(struct gg_http *h) -{ - struct gg_pubdir *p; - char *tmp; - - if (!h) { - errno = EFAULT; - return -1; - } - - if (h->state == GG_STATE_ERROR) { - gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n"); - errno = EINVAL; - return -1; - } - - if (h->state != GG_STATE_PARSING) { - if (gg_http_watch_fd(h) == -1) { - gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n"); - errno = EINVAL; - return -1; - } - } - - if (h->state != GG_STATE_PARSING) - return 0; - - h->state = GG_STATE_DONE; - - if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) { - gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n"); - return -1; - } - - p->success = 0; - p->uin = 0; - - gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body); - - if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) { - p->success = 1; - p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0); - gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin); - } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { - p->success = 1; - if (tmp[7] == ':') - p->uin = strtol(tmp + 8, NULL, 0); - gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin); - } else - gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n"); - - return 0; -} - -/* - * gg_pubdir_free() - * - * zwalnia pami�� po efektach operacji na katalogu publicznym. - * - * - h - zwalniana struktura - */ -void gg_pubdir_free(struct gg_http *h) -{ - if (!h) - return; - - free(h->data); - gg_http_free(h); -} - -/* - * gg_token() - * - * pobiera z serwera token do autoryzacji zak�adania konta, usuwania - * konta i zmiany has�a. - * - * zaalokowana struct gg_http, kt�r� po�niej nale�y zwolni� - * funkcj� gg_token_free(), albo NULL je�li wyst�pi� b��d. - */ -struct gg_http *gg_token(int async) -{ - struct gg_http *h; - const char *query; - - query = "Host: " GG_REGISTER_HOST "\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Content-Length: 0\r\n" - "Pragma: no-cache\r\n" - "\r\n"; - - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) { - gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n"); - return NULL; - } - - h->type = GG_SESSION_TOKEN; - - h->callback = gg_token_watch_fd; - h->destroy = gg_token_free; - - if (!async) - gg_token_watch_fd(h); - - return h; -} - -/* - * gg_token_watch_fd() - * - * przy asynchronicznych operacjach zwi�zanych z tokenem nale�y wywo�ywa� - * t� funkcj� przy zmianach na obserwowanym deskryptorze. - * - * - h - struktura opisuj�ca po��czenie - * - * je�li wszystko posz�o dobrze to 0, inaczej -1. operacja b�dzie - * zako�czona, je�li h->state == GG_STATE_DONE. je�li wyst�pi jaki� - * b��d, to b�dzie tam GG_STATE_ERROR i odpowiedni kod b��du w h->error. - */ -int gg_token_watch_fd(struct gg_http *h) -{ - if (!h) { - errno = EFAULT; - return -1; - } - - if (h->state == GG_STATE_ERROR) { - gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n"); - errno = EINVAL; - return -1; - } - - if (h->state != GG_STATE_PARSING) { - if (gg_http_watch_fd(h) == -1) { - gg_debug(GG_DEBUG_MISC, "=> token, http failure\n"); - errno = EINVAL; - return -1; - } - } - - if (h->state != GG_STATE_PARSING) - return 0; - - /* je�li h->data jest puste, to �ci�gali�my tokenid i url do niego, - * ale je�li co� tam jest, to znaczy, �e mamy drugi etap polegaj�cy - * na pobieraniu tokenu. */ - if (!h->data) { - int width, height, length; - char *url = NULL, *tokenid = NULL, *path, *headers; - const char *host; - struct gg_http *h2; - struct gg_token *t; - - gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body); - - if (h->body && (!(url = malloc(strlen(h->body))) || !(tokenid = malloc(strlen(h->body))))) { - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n"); - free(url); - return -1; - } - - if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) { - gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n"); - free(url); - free(tokenid); - errno = EINVAL; - return -1; - } - - /* dostali�my tokenid i wszystkie niezb�dne informacje, - * wi�c pobierzmy obrazek z tokenem */ - - if (strncmp(url, "http://", 7)) { - path = gg_saprintf("%s?tokenid=%s", url, tokenid); - host = GG_REGISTER_HOST; - } else { - char *slash = (char*)strchr(url + 7, '/'); - - if (slash) { - path = gg_saprintf("%s?tokenid=%s", slash, tokenid); - *slash = 0; - host = url + 7; - } else { - gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n"); - free(url); - free(tokenid); - errno = EINVAL; - return -1; - } - } - - if (!path) { - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); - free(url); - free(tokenid); - return -1; - } - - if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) { - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); - free(path); - free(url); - free(tokenid); - return -1; - } - - if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) { - gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n"); - free(headers); - free(url); - free(path); - free(tokenid); - return -1; - } - - free(headers); - free(path); - free(url); - - memcpy(h, h2, sizeof(struct gg_http)); - free(h2); - - h->type = GG_SESSION_TOKEN; - - h->callback = gg_token_watch_fd; - h->destroy = gg_token_free; - - if (!h->async) - gg_token_watch_fd(h); - - if (!(h->data = t = malloc(sizeof(struct gg_token)))) { - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n"); - free(tokenid); - return -1; - } - - t->width = width; - t->height = height; - t->length = length; - t->tokenid = tokenid; - } else { - /* obrazek mamy w h->body */ - h->state = GG_STATE_DONE; - } - - return 0; -} - -/* - * gg_token_free() - * - * zwalnia pami�� po efektach pobierania tokenu. - * - * - h - zwalniana struktura - */ -void gg_token_free(struct gg_http *h) -{ - struct gg_token *t; - - if (!h) - return; - - if ((t = h->data)) - free(t->tokenid); - - free(h->data); - gg_http_free(h); -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/libgadu/pubdir50.c b/kopete/protocols/gadu/libgadu/pubdir50.c deleted file mode 100644 index 877ab83e..00000000 --- a/kopete/protocols/gadu/libgadu/pubdir50.c +++ /dev/null @@ -1,467 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "libgadu.h" - -/* - * gg_pubdir50_new() - * - * tworzy now± zmienn± typu gg_pubdir50_t. - * - * zaalokowana zmienna lub NULL w przypadku braku pamiêci. - */ -gg_pubdir50_t gg_pubdir50_new(int type) -{ - gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s)); - - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type); - - if (!res) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n"); - return NULL; - } - - memset(res, 0, sizeof(struct gg_pubdir50_s)); - - res->type = type; - - return res; -} - -/* - * gg_pubdir50_add_n() // funkcja wewnêtrzna - * - * funkcja dodaje lub zastêpuje istniej±ce pole do zapytania lub odpowiedzi. - * - * - req - wska¼nik opisu zapytania, - * - num - numer wyniku (0 dla zapytania), - * - field - nazwa pola, - * - value - warto¶æ pola, - * - * 0/-1 - */ -static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value) -{ - struct gg_pubdir50_entry *tmp = NULL, *entry; - char *dupfield, *dupvalue; - int i; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value); - - if (!(dupvalue = strdup(value))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); - return -1; - } - - for (i = 0; i < req->entries_count; i++) { - if (req->entries[i].num != num || strcmp(req->entries[i].field, field)) - continue; - - free(req->entries[i].value); - req->entries[i].value = dupvalue; - - return 0; - } - - if (!(dupfield = strdup(field))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); - free(dupvalue); - return -1; - } - - if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); - free(dupfield); - free(dupvalue); - return -1; - } - - req->entries = tmp; - - entry = &req->entries[req->entries_count]; - entry->num = num; - entry->field = dupfield; - entry->value = dupvalue; - - req->entries_count++; - - return 0; -} - -/* - * gg_pubdir50_add() - * - * funkcja dodaje pole do zapytania. - * - * - req - wska¼nik opisu zapytania, - * - field - nazwa pola, - * - value - warto¶æ pola, - * - * 0/-1 - */ -int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value) -{ - return gg_pubdir50_add_n(req, 0, field, value); -} - -/* - * gg_pubdir50_seq_set() - * - * ustawia numer sekwencyjny zapytania. - * - * - req - zapytanie, - * - seq - nowy numer sekwencyjny. - * - * 0/-1. - */ -int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq); - - if (!req) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - req->seq = seq; - - return 0; -} - -/* - * gg_pubdir50_free() - * - * zwalnia pamiêæ po zapytaniu lub rezultacie szukania u¿ytkownika. - * - * - s - zwalniana zmienna, - */ -void gg_pubdir50_free(gg_pubdir50_t s) -{ - int i; - - if (!s) - return; - - for (i = 0; i < s->entries_count; i++) { - free(s->entries[i].field); - free(s->entries[i].value); - } - - free(s->entries); - free(s); -} - -/* - * gg_pubdir50() - * - * wysy³a zapytanie katalogu publicznego do serwera. - * - * - sess - sesja, - * - req - zapytanie. - * - * numer sekwencyjny wyszukiwania lub 0 w przypadku b³êdu. - */ -uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) -{ - int i, size = 5; - uint32_t res; - char *buf, *p; - struct gg_pubdir50_request *r; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); - - if (!sess || !req) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); - errno = EFAULT; - return 0; - } - - if (sess->state != GG_STATE_CONNECTED) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); - errno = ENOTCONN; - return 0; - } - - for (i = 0; i < req->entries_count; i++) { - /* wyszukiwanie bierze tylko pierwszy wpis */ - if (req->entries[i].num) - continue; - - size += strlen(req->entries[i].field) + 1; - size += strlen(req->entries[i].value) + 1; - } - - if (!(buf = malloc(size))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); - return 0; - } - - r = (struct gg_pubdir50_request*) buf; - res = time(NULL); - r->type = req->type; - r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL)); - req->seq = gg_fix32(r->seq); - - for (i = 0, p = buf + 5; i < req->entries_count; i++) { - if (req->entries[i].num) - continue; - - strcpy(p, req->entries[i].field); - p += strlen(p) + 1; - - strcpy(p, req->entries[i].value); - p += strlen(p) + 1; - } - - if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1) - res = 0; - - free(buf); - - return res; -} - -/* - * gg_pubdir50_handle_reply() // funkcja wewnêtrzna - * - * analizuje przychodz±cy pakiet odpowiedzi i zapisuje wynik w struct gg_event. - * - * - e - opis zdarzenia - * - packet - zawarto¶æ pakietu odpowiedzi - * - length - d³ugo¶æ pakietu odpowiedzi - * - * 0/-1 - */ -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) -{ - const char *end = packet + length, *p; - struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; - gg_pubdir50_t res; - int num = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length); - - if (!e || !packet) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (length < 5) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n"); - errno = EINVAL; - return -1; - } - - if (!(res = gg_pubdir50_new(r->type))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n"); - return -1; - } - - e->event.pubdir50 = res; - - res->seq = gg_fix32(r->seq); - - switch (res->type) { - case GG_PUBDIR50_READ: - e->type = GG_EVENT_PUBDIR50_READ; - break; - - case GG_PUBDIR50_WRITE: - e->type = GG_EVENT_PUBDIR50_WRITE; - break; - - default: - e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY; - break; - } - - /* brak wyników? */ - if (length == 5) - return 0; - - /* pomiñ pocz±tek odpowiedzi */ - p = packet + 5; - - while (p < end) { - const char *field, *value; - - field = p; - - /* sprawd¼, czy nie mamy podzia³u na kolejne pole */ - if (!*field) { - num++; - field++; - } - - value = NULL; - - for (p = field; p < end; p++) { - /* je¶li mamy koniec tekstu... */ - if (!*p) { - /* ...i jeszcze nie mieli¶my warto¶ci pola to - * wiemy, ¿e po tym zerze jest warto¶æ... */ - if (!value) - value = p + 1; - else - /* ...w przeciwym wypadku koniec - * warto¶ci i mo¿emy wychodziæ - * grzecznie z pêtli */ - break; - } - } - - /* sprawd¼my, czy pole nie wychodzi poza pakiet, ¿eby nie - * mieæ segfaultów, je¶li serwer przestanie zakañczaæ pakietów - * przez \0 */ - - if (p == end) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n"); - goto failure; - } - - p++; - - /* je¶li dostali¶my namier na nastêpne wyniki, to znaczy ¿e - * mamy koniec wyników i nie jest to kolejna osoba. */ - if (!strcasecmp(field, "nextstart")) { - res->next = atoi(value); - num--; - } else { - if (gg_pubdir50_add_n(res, num, field, value) == -1) - goto failure; - } - } - - res->count = num + 1; - - return 0; - -failure: - gg_pubdir50_free(res); - return -1; -} - -/* - * gg_pubdir50_get() - * - * pobiera informacjê z rezultatu wyszukiwania. - * - * - res - rezultat wyszukiwania, - * - num - numer odpowiedzi, - * - field - nazwa pola (wielko¶æ liter nie ma znaczenia). - * - * warto¶æ pola lub NULL, je¶li nie znaleziono. - */ -const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field) -{ - char *value = NULL; - int i; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field); - - if (!res || num < 0 || !field) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n"); - errno = EINVAL; - return NULL; - } - - for (i = 0; i < res->entries_count; i++) { - if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) { - value = res->entries[i].value; - break; - } - } - - return value; -} - -/* - * gg_pubdir50_count() - * - * zwraca ilo¶æ wyników danego zapytania. - * - * - res - odpowied¼ - * - * ilo¶æ lub -1 w przypadku b³êdu. - */ -int gg_pubdir50_count(gg_pubdir50_t res) -{ - return (!res) ? -1 : res->count; -} - -/* - * gg_pubdir50_type() - * - * zwraca rodzaj zapytania lub odpowiedzi. - * - * - res - zapytanie lub odpowied¼ - * - * ilo¶æ lub -1 w przypadku b³êdu. - */ -int gg_pubdir50_type(gg_pubdir50_t res) -{ - return (!res) ? -1 : res->type; -} - -/* - * gg_pubdir50_next() - * - * zwraca numer, od którego nale¿y rozpocz±æ kolejne wyszukiwanie, je¶li - * zale¿y nam na kolejnych wynikach. - * - * - res - odpowied¼ - * - * numer lub -1 w przypadku b³êdu. - */ -uin_t gg_pubdir50_next(gg_pubdir50_t res) -{ - return (!res) ? (unsigned) -1 : res->next; -} - -/* - * gg_pubdir50_seq() - * - * zwraca numer sekwencyjny zapytania lub odpowiedzi. - * - * - res - zapytanie lub odpowied¼ - * - * numer lub -1 w przypadku b³êdu. - */ -uint32_t gg_pubdir50_seq(gg_pubdir50_t res) -{ - return (!res) ? (unsigned) -1 : res->seq; -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ diff --git a/kopete/protocols/gadu/ui/CMakeLists.txt b/kopete/protocols/gadu/ui/CMakeLists.txt new file mode 100644 index 00000000..10b5b07d --- /dev/null +++ b/kopete/protocols/gadu/ui/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### gaduui (static) ########################### + +tde_add_library( gaduui STATIC_PIC AUTOMOC + SOURCES + gaduadd.ui gadusearch.ui gadueditaccountui.ui gaduawayui.ui + gaduregisteraccountui.ui empty.cpp +) diff --git a/kopete/protocols/groupwise/CMakeLists.txt b/kopete/protocols/groupwise/CMakeLists.txt new file mode 100644 index 00000000..c6df3323 --- /dev/null +++ b/kopete/protocols/groupwise/CMakeLists.txt @@ -0,0 +1,50 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) +add_subdirectory( libgroupwise ) +add_subdirectory( ui ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/libgroupwise + ${CMAKE_CURRENT_SOURCE_DIR}/libgroupwise/tasks + ${CMAKE_CURRENT_SOURCE_DIR}/libgroupwise/qca/src + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_groupwise.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES gwchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_groupwise ) + + +##### kopete_groupwise (module) ################# + +tde_add_kpart( kopete_groupwise AUTOMOC + SOURCES + gwprotocol.cpp gwcontact.cpp gwaccount.cpp gwbytestream.cpp + gwconnector.cpp gwmessagemanager.cpp gwcontactlist.cpp + LINK + kopetegroupwiseui-static groupwise-static groupwise_tasks-static + groupwise_qca-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/groupwise/icons/CMakeLists.txt b/kopete/protocols/groupwise/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/groupwise/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/groupwise/libgroupwise/CMakeLists.txt b/kopete/protocols/groupwise/libgroupwise/CMakeLists.txt new file mode 100644 index 00000000..ef158cd9 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/CMakeLists.txt @@ -0,0 +1,53 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( qca ) +add_subdirectory( tasks ) + +# add_definitions( +# -DUSE_TLSHANDLER +# ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/qca/src + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### groupwise (static) ######################## + +tde_add_library( groupwise STATIC_PIC AUTOMOC + SOURCES + bytestream.cpp chatroommanager.cpp client.cpp connector.cpp + coreprotocol.cpp eventprotocol.cpp eventtransfer.cpp + gwclientstream.cpp gwerror.cpp gwfield.cpp gwglobal.cpp + inputprotocolbase.cpp privacymanager.cpp qcatlshandler.cpp + request.cpp requestfactory.cpp response.cpp responseprotocol.cpp + rtf.cc safedelete.cpp securestream.cpp stream.cpp task.cpp + tlshandler.cpp transfer.cpp transferbase.cpp userdetailsmanager.cpp + usertransfer.cpp +) + + +##### gwtest (static) ########################### + +tde_add_library( gwtest STATIC_PIC AUTOMOC + SOURCES + coreprotocol.cpp eventtransfer.cpp gwfield.cpp request.cpp + requestfactory.cpp transfer.cpp usertransfer.cpp client.cpp task.cpp + safedelete.cpp gwclientstream.cpp qcatlshandler.cpp stream.cpp + tlshandler.cpp response.cpp connector.cpp securestream.cpp + bytestream.cpp +) diff --git a/kopete/protocols/groupwise/libgroupwise/qca/CMakeLists.txt b/kopete/protocols/groupwise/libgroupwise/qca/CMakeLists.txt new file mode 100644 index 00000000..7356f221 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( src ) diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/CMakeLists.txt b/kopete/protocols/groupwise/libgroupwise/qca/src/CMakeLists.txt new file mode 100644 index 00000000..9c74e339 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/src/CMakeLists.txt @@ -0,0 +1,22 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### groupwise_qca (static) #################### + +tde_add_library( groupwise_qca STATIC_PIC AUTOMOC + SOURCES qca.cpp +) diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/CMakeLists.txt b/kopete/protocols/groupwise/libgroupwise/tasks/CMakeLists.txt new file mode 100644 index 00000000..1fc4a60a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../qca/src + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### groupwise_tasks (static) ################## + +tde_add_library( groupwise_tasks STATIC_PIC AUTOMOC + SOURCES + requesttask.cpp eventtask.cpp logintask.cpp setstatustask.cpp + statustask.cpp conferencetask.cpp createconferencetask.cpp + sendmessagetask.cpp getdetailstask.cpp getstatustask.cpp + typingtask.cpp connectiontask.cpp sendinvitetask.cpp + joinconferencetask.cpp leaveconferencetask.cpp rejectinvitetask.cpp + keepalivetask.cpp createcontacttask.cpp modifycontactlisttask.cpp + createfoldertask.cpp movecontacttask.cpp updateitemtask.cpp + createcontactinstancetask.cpp deleteitemtask.cpp updatefoldertask.cpp + updatecontacttask.cpp pollsearchresultstask.cpp privacyitemtask.cpp + needfoldertask.cpp searchchattask.cpp searchusertask.cpp + searchusertask.h getchatsearchresultstask.cpp chatcountstask.cpp + chatpropertiestask.cpp joinchattask.cpp +) diff --git a/kopete/protocols/groupwise/ui/CMakeLists.txt b/kopete/protocols/groupwise/ui/CMakeLists.txt new file mode 100644 index 00000000..2045bef7 --- /dev/null +++ b/kopete/protocols/groupwise/ui/CMakeLists.txt @@ -0,0 +1,35 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../libgroupwise + ${CMAKE_CURRENT_SOURCE_DIR}/../libgroupwise/qca/src + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetegroupwiseui (static) ################ + +tde_add_library( kopetegroupwiseui STATIC_PIC AUTOMOC + SOURCES + gwaccountpreferences.ui gwaddcontactpage.cpp gwaddui.ui + gweditaccountwidget.cpp gwreceiveinvitationdialog.cpp + gwshowinvitation.ui gwcontactpropswidget.ui gwcontactproperties.cpp + gwprivacy.ui gwprivacydialog.cpp gwsearch.cpp gwcustomstatuswidget.ui + gwcustomstatusedit.ui gwcontactsearch.ui gwchatsearchwidget.ui + gwchatsearchdialog.cpp gwchatpropswidget.ui gwchatpropsdialog.cpp +) diff --git a/kopete/protocols/irc/CMakeLists.txt b/kopete/protocols/irc/CMakeLists.txt new file mode 100644 index 00000000..6c795704 --- /dev/null +++ b/kopete/protocols/irc/CMakeLists.txt @@ -0,0 +1,54 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) +add_subdirectory( libkirc ) +add_subdirectory( ui ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/libkirc + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_irc.desktop irc.protocol + DESTINATION ${SERVICES_INSTALL_DIR} ) + +install( FILES + ircnetworks.xml ircchatui.rc + DESTINATION ${DATA_INSTALL_DIR}/kopete ) + + +##### kopete_irc (module) ####################### + +tde_add_kpart( kopete_irc AUTOMOC + SOURCES + ircaccount.cpp ircaddcontactpage.cpp ircchannelcontact.cpp + irccontact.cpp ircguiclient.cpp ircprotocol.cpp ircservercontact.cpp + ircsignalhandler.cpp irctransferhandler.cpp ircusercontact.cpp + irccontactmanager.cpp kcodecaction.cpp ksparser.cpp + LINK + kopeteircui-static kirc-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/irc/icons/CMakeLists.txt b/kopete/protocols/irc/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/irc/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/irc/libkirc/CMakeLists.txt b/kopete/protocols/irc/libkirc/CMakeLists.txt new file mode 100644 index 00000000..d2b298c2 --- /dev/null +++ b/kopete/protocols/irc/libkirc/CMakeLists.txt @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kirc (static) ############################# + +tde_add_library( kirc STATIC_PIC AUTOMOC + SOURCES + kircengine.cpp kircengine_commands.cpp kircengine_ctcp.cpp + kircengine_numericreplies.cpp kircentity.cpp kircmessage.cpp + kircmessageredirector.cpp kirctransfer.cpp kirctransferhandler.cpp + kirctransferserver.cpp ksslsocket.cpp +) diff --git a/kopete/protocols/irc/libkirc/kircentity.h b/kopete/protocols/irc/libkirc/kircentity.h index d802d8f4..1878a406 100644 --- a/kopete/protocols/irc/libkirc/kircentity.h +++ b/kopete/protocols/irc/libkirc/kircentity.h @@ -39,7 +39,7 @@ class Entity Q_OBJECT public: - typedef enum Type + enum Type { Unknown, Server, diff --git a/kopete/protocols/irc/ui/CMakeLists.txt b/kopete/protocols/irc/ui/CMakeLists.txt new file mode 100644 index 00000000..7d77d5b8 --- /dev/null +++ b/kopete/protocols/irc/ui/CMakeLists.txt @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../libkirc + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteircui (static) ###################### + +tde_add_library( kopeteircui STATIC_PIC AUTOMOC + SOURCES + ircadd.ui empty.cpp irceditaccountwidget.cpp irceditaccount.ui + channellist.cpp channellistdialog.cpp networkconfig.ui +) diff --git a/kopete/protocols/jabber/CMakeLists.txt b/kopete/protocols/jabber/CMakeLists.txt new file mode 100644 index 00000000..d3fb6d79 --- /dev/null +++ b/kopete/protocols/jabber/CMakeLists.txt @@ -0,0 +1,85 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) +add_subdirectory( icons ) +add_subdirectory( libiris ) +add_subdirectory( kioslave ) + +if( WITH_JINGLE ) + add_subdirectory( jingle ) + set( SUPPORT_JINGLE 1 CACHE INTERNAL "" FORCE ) + set( JINGLE_LIBRARIES + kopetejabberjingle-static cricketsessionphone-static cricketxmllite-static + cricketp2pclient-static cricketxmpp-static cricketp2pbase-static cricketbase-static + mediastreamer-static ortp-static + ${GLIB2_LIBRARIES} ${GTHREAD2_LIBRARIES} ${GMODULE2_LIBRARIES} + ${EXPAT_LIBRARY} ${SPEEX_LIBRARIES} pthread + ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_BINARY_DIR}/jingle + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/iris/include + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/iris/jabber + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/iris/xmpp-im + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/qca/src + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/cutestuff/util + ${CMAKE_CURRENT_SOURCE_DIR}/libiris/cutestuff/network + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/jingle + ${CMAKE_CURRENT_SOURCE_DIR}/../../libkopete + ${CMAKE_CURRENT_SOURCE_DIR}/../../libkopete/ui + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_jabber.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES jabberchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_jabber ) + + +##### jabberclient (static) ##################### + +tde_add_library( jabberclient STATIC_PIC AUTOMOC + SOURCES + jabberclient.cpp jabberconnector.cpp jabberbytestream.cpp +) + + +##### kopete_jabber (module) #################### + +tde_add_kpart( kopete_jabber AUTOMOC + SOURCES + jabberprotocol.cpp jabberaccount.cpp jabberresource.cpp + jabberresourcepool.cpp jabberbasecontact.cpp jabbercontact.cpp + jabbergroupcontact.cpp jabbergroupmembercontact.cpp + jabbercontactpool.cpp jabberformtranslator.cpp jabberformlineedit.cpp + jabberchatsession.cpp jabbergroupchatmanager.cpp + jabberfiletransfer.cpp jabbercapabilitiesmanager.cpp + jabbertransport.cpp jabberbookmarks.cpp + LINK + jabberclient-static kopetejabberui-static + iris_jabber-static iris_xmpp_core-static iris_xmpp_im-static iris-static + qca-static cutestuff_network-static cutestuff_util-static ${JINGLE_LIBRARIES} + kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/jabber/icons/CMakeLists.txt b/kopete/protocols/jabber/icons/CMakeLists.txt new file mode 100644 index 00000000..37a5c370 --- /dev/null +++ b/kopete/protocols/jabber/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( ) diff --git a/kopete/protocols/jabber/jingle/CMakeLists.txt b/kopete/protocols/jabber/jingle/CMakeLists.txt new file mode 100644 index 00000000..18bbf758 --- /dev/null +++ b/kopete/protocols/jabber/jingle/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( libjingle ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/libjingle + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/include + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/xmpp-im + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/jabber + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/cutestuff/util + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetejabberjingle (static) ############### + +tde_moc( MOCS voicecaller.h ) + +tde_add_library( kopetejabberjingle STATIC_PIC AUTOMOC + SOURCES + jinglevoicecaller.cpp jinglevoicesessiondialogbase.ui + jinglevoicesessiondialog.cpp ${MOCS} +) diff --git a/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp index dd809cea..12e4b93d 100644 --- a/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp +++ b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp @@ -374,3 +374,5 @@ cricket::Thread* JingleVoiceCaller::thread_ = NULL; cricket::NetworkManager* JingleVoiceCaller::network_manager_ = NULL; cricket::BasicPortAllocator* JingleVoiceCaller::port_allocator_ = NULL; cricket::SocketAddress* JingleVoiceCaller::stun_addr_ = NULL; + +#include "jinglevoicecaller.moc" diff --git a/kopete/protocols/jabber/jingle/libjingle/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/CMakeLists.txt new file mode 100644 index 00000000..0aa34648 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( talk ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/CMakeLists.txt new file mode 100644 index 00000000..1a1d0416 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/CMakeLists.txt @@ -0,0 +1,19 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +add_subdirectory( base ) +add_subdirectory( p2p ) +add_subdirectory( xmllite ) +add_subdirectory( xmpp ) +add_subdirectory( session ) +add_subdirectory( third_party ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/ConfigureChecks.cmake b/kopete/protocols/jabber/jingle/libjingle/talk/ConfigureChecks.cmake new file mode 100644 index 00000000..396efa92 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/ConfigureChecks.cmake @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# gthread-2.0 +pkg_search_module( GTHREAD2 gthread-2.0 ) +if( NOT GTHREAD2_FOUND ) + tde_message_fatal( "gthread-2.0 is required, but was not found on your system" ) +endif( ) + + +# gmodule-2.0 +pkg_search_module( GMODULE2 gmodule-2.0 ) +if( NOT GMODULE2_FOUND ) + tde_message_fatal( "gmodule-2.0 are required, but not found on your system" ) +endif( ) + + +# speex +if( WITH_SPEEX ) + pkg_search_module( SPEEX speex ) + if( SPEEX_FOUND ) + set( HAVE_SPEEX 1 CACHE INTERNAL "" FORCE ) + if( NOT SPEEX_INCLUDE_DIRS ) + set( SPEEX_INCLUDE_DIRS "/usr/include/speex" ) + endif( ) + else( ) + tde_message_fatal( "speex is required, but was not found on your system" ) + endif( ) +endif( ) + +# expat +check_include_file( expat.h HAVE_EXPAT_H ) +if( HAVE_EXPAT_H ) + check_library_exists( expat XML_ParserCreate "" HAVE_EXPAT ) +endif( ) +if( HAVE_EXPAT_H AND HAVE_EXPAT ) + set( EXPAT_LIBRARY expat CACHE INTERNAL "" FORCE ) +else( ) + tde_message_fatal( "expat is required, but was not found on your system" ) +endif( ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt new file mode 100644 index 00000000..8f037a9a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt @@ -0,0 +1,32 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DPOSIX +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cricketbase (static) ###################### + +tde_add_library( cricketbase STATIC_PIC + SOURCES + socketaddress.cc jtime.cc asyncudpsocket.cc messagequeue.cc + thread.cc physicalsocketserver.cc bytebuffer.cc asyncpacketsocket.cc + network.cc asynctcpsocket.cc socketadapters.cc md5c.c base64.cc + task.cc taskrunner.cc host.cc socketaddresspair.cc +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc index 6d4697a6..8bf66a38 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc @@ -32,6 +32,7 @@ #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/logging.h" +#include <cstring> #if defined(_MSC_VER) && _MSC_VER < 1300 namespace std { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc index 067f50ed..e3af08b7 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc @@ -30,6 +30,7 @@ #include "talk/base/byteorder.h" #include <algorithm> #include <cassert> +#include <cstring> #if defined(_MSC_VER) && _MSC_VER < 1300 namespace std { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc index 7b7490d9..f604050f 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc @@ -30,6 +30,8 @@ #include "talk/base/network.h" #include "talk/base/socket.h" #include <string> +#include <cstring> +#include <cstdlib> #include <iostream> #include <cassert> #include <errno.h> diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h index 2a9cbed6..9b35b9af 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h @@ -32,6 +32,7 @@ #include "talk/base/criticalsection.h" #include "talk/base/socketserver.h" #include "talk/base/jtime.h" +#include <string.h> #include <vector> #include <queue> #include <algorithm> diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc index 91d2daad..37836302 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc @@ -30,6 +30,7 @@ #endif #include <cassert> +#include <algorithm> #ifdef POSIX extern "C" { @@ -37,6 +38,7 @@ extern "C" { #include <fcntl.h> #include <sys/time.h> #include <unistd.h> +#include <string.h> } #endif @@ -59,9 +61,6 @@ extern "C" { #include <windows.h> #undef SetPort -#include <algorithm> -#include <iostream> - class WinsockInitializer { public: WinsockInitializer() { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc index 049e923c..f57043e3 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc @@ -42,6 +42,7 @@ #endif #include <cassert> +#include <cstring> #include "talk/base/base64.h" #include "talk/base/basicdefs.h" diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/CMakeLists.txt new file mode 100644 index 00000000..1708f179 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( base ) +add_subdirectory( client ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt new file mode 100644 index 00000000..ba05a061 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt @@ -0,0 +1,52 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DPOSIX +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cricketp2pbase (static) ################### + +tde_add_library( cricketp2pbase STATIC_PIC + SOURCES + stun.cc port.cc udpport.cc tcpport.cc helpers.cc sessionmanager.cc + session.cc p2psocket.cc relayport.cc stunrequest.cc stunport.cc + socketmanager.cc +) + + +##### relayserver (executable) ################## + +tde_add_executable( relayserver + SOURCES + relayserver.cc relayserver_main.cc + LINK + cricketbase-static cricketp2pbase-static pthread + DESTINATION ${BIN_INSTALL_DIR} +) + + +##### stunserver (executable) ################### + +tde_add_executable( stunserver + SOURCES + stunserver.cc stunserver_main.cc + LINK + cricketbase-static cricketp2pbase-static pthread + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc index 5f624f37..4dfae42c 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc @@ -30,6 +30,7 @@ #include "talk/p2p/base/relayserver.h" #include <iostream> #include <assert.h> +#include <cstring> #ifdef POSIX extern "C" { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc index 6a22b238..2d6aa67c 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc @@ -29,6 +29,7 @@ #include "talk/p2p/base/stun.h" #include <iostream> #include <cassert> +#include <cstring> #if defined(_MSC_VER) && _MSC_VER < 1300 namespace std { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc index 6e4f6b66..c6d9f9f8 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc @@ -28,6 +28,7 @@ #include "talk/base/bytebuffer.h" #include "talk/p2p/base/stunserver.h" #include <iostream> +#include <cstring> #ifdef POSIX extern "C" { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc index bd8a96e5..bac3e35f 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc @@ -29,6 +29,7 @@ #include "talk/base/thread.h" #include "talk/p2p/base/stunserver.h" #include <iostream> +#include <cstring> #ifdef POSIX extern "C" { diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt new file mode 100644 index 00000000..7ede9820 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DLINUX + -DPOSIX + -DINTERNAL_BUILD +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cricketp2pclient (static) ################# + +tde_add_library( cricketp2pclient STATIC_PIC + SOURCES + sessionclient.cc basicportallocator.cc socketmonitor.cc +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/session/CMakeLists.txt new file mode 100644 index 00000000..0b78949f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( phone ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt new file mode 100644 index 00000000..3f22e535 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt @@ -0,0 +1,34 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DPOSIX +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/ortp + ${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/mediastreamer + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIRS} + ${SPEEX_INCLUDE_DIRS} +) + + +##### cricketsessionphone (static) ############## + +tde_add_library( cricketsessionphone STATIC_PIC + SOURCES + audiomonitor.cc channelmanager.cc voicechannel.cc call.cc + phonesessionclient.cc linphonemediaengine.cc +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc index e363392c..f3244c54 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc @@ -27,7 +27,7 @@ extern "C" { #include "talk/third_party/mediastreamer/msspeexdec.h" #endif } -#include <ortp/ortp.h> +#include <ortp.h> #include <netdb.h> #include <unistd.h> #include <fcntl.h> diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc index d8a31df2..7f2ff11f 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc @@ -25,6 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <stdio.h> + #include "talk/base/logging.h" #include "talk/session/receiver.h" #include "talk/session/phone/phonesessionclient.h" diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/CMakeLists.txt new file mode 100644 index 00000000..da1647c0 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( mediastreamer ) +add_subdirectory( ortp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/CMakeLists.txt new file mode 100644 index 00000000..8c5a923b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/CMakeLists.txt @@ -0,0 +1,36 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DG_LOG_DOMAIN="MediaStreamer" +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../ortp + ${CMAKE_BINARY_DIR} + ${GLIB2_INCLUDE_DIRS} + ${SPEEX_INCLUDE_DIRS} +) + + +##### mediastreamer (static) #################### + +tde_add_library( mediastreamer STATIC_PIC + SOURCES + msfilter.c mscodec.c mssoundread.c mssoundwrite.c msbuffer.c + msqueue.c msfifo.c ms.c mssync.c msnosync.c msread.c mswrite.c + mscopy.c msosswrite.c msossread.c msringplayer.c msrtprecv.c + msrtpsend.c msAlawenc.c msAlawdec.c msMUlawenc.c msMUlawdec.c + mstimer.c msqdispatcher.c msfdispatcher.c sndcard.c osscard.c + hpuxsndcard.c alsacard.c jackcard.c audiostream.c msspeexenc.c + msspeexdec.c msilbcdec.c msilbcenc.c +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h index 5c8b616c..0f36c379 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h @@ -28,7 +28,7 @@ /* because of a conflict between config.h from oRTP and config.h from linphone:*/ #undef PACKAGE #undef VERSION -#include <ortp/ortp.h> +#include <ortp.h> /*this is the class that implements a copy filter*/ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c index b13dfe28..645a19cd 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c @@ -19,7 +19,7 @@ */ #include "msrtpsend.h" -#include <ortp/telephonyevents.h> +#include <telephonyevents.h> #include "mssync.h" #include "mscodec.h" diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h index 746e436d..96889964 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h @@ -27,7 +27,7 @@ #undef PACKAGE #undef VERSION -#include <ortp/ortp.h> +#include <ortp.h> /*this is the class that implements a sending through rtp filter*/ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/CMakeLists.txt new file mode 100644 index 00000000..92a73a36 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/CMakeLists.txt @@ -0,0 +1,32 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -D_ORTP_SOURCE + -DG_LOG_DOMAIN="oRTP" +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${GLIB2_INCLUDE_DIRS} +) + + +##### ortp (static) ############################# + +tde_add_library( ortp STATIC_PIC + SOURCES + port_fct.c rtpmod.c rtpparse.c rtpsession.c jitterctl.c + rtpsignaltable.c rtptimer.c posixtimer.c ortp.c scheduler.c + avprofile.c sessionset.c telephonyevents.c payloadtype.c rtcp.c + utils.c rtcpparse.c str_utils.c +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/avprofile.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/avprofile.c new file mode 100644 index 00000000..8917e21b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/avprofile.c @@ -0,0 +1,281 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <payloadtype.h> + +char offset127=127; +char offset0xD5=0xD5; +char offset0=0; + +/* IMPORTANT */ +/* some compiler don't support the "field:" syntax. Those macros are there to trap the problem +This means that if you want to keep portability, payload types must be defined with their fields +in the right order. */ + +#if !defined(__hpux) && !defined(WIN32) + +#define TYPE(val) type: (val) +#define CLOCK_RATE(val) clock_rate: (val) +#define BITS_PER_SAMPLE(val) bits_per_sample: (val) +#define ZERO_PATTERN(val) zero_pattern: (val) +#define PATTERN_LENGTH(val) pattern_length: (val) +#define NORMAL_BITRATE(val) normal_bitrate: (val) +#define MIME_TYPE(val) mime_type: (val) +#define FMTP(val) FMTP : (val) + +#else + +#define TYPE(val) (val) +#define CLOCK_RATE(val) (val) +#define BITS_PER_SAMPLE(val) (val) +#define ZERO_PATTERN(val) (val) +#define PATTERN_LENGTH(val) (val) +#define NORMAL_BITRATE(val) (val) +#define MIME_TYPE(val) (val) +#define FMTP(val) (val) + +#endif + +PayloadType pcmu8000={ + TYPE( PAYLOAD_AUDIO_CONTINUOUS), + CLOCK_RATE( 8000), + BITS_PER_SAMPLE(8), + ZERO_PATTERN( &offset127), + PATTERN_LENGTH( 1), + NORMAL_BITRATE( 64000), + MIME_TYPE ("PCMU") +}; + +PayloadType pcma8000={ + TYPE( PAYLOAD_AUDIO_CONTINUOUS), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(8), + ZERO_PATTERN( &offset0xD5), + PATTERN_LENGTH( 1), + NORMAL_BITRATE( 64000), + MIME_TYPE ("PCMA") +}; + +PayloadType pcm8000={ + TYPE( PAYLOAD_AUDIO_CONTINUOUS), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(16), + ZERO_PATTERN( &offset0), + PATTERN_LENGTH(1), + NORMAL_BITRATE( 128000), + MIME_TYPE ("PCM") +}; + +PayloadType lpc1016={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE( 0), + ZERO_PATTERN( NULL), + PATTERN_LENGTH( 0), + NORMAL_BITRATE( 2400), + MIME_TYPE ("1016") +}; + + +PayloadType gsm= +{ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE( 0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH( 0), + NORMAL_BITRATE( 13500), + MIME_TYPE ("GSM") +}; + +PayloadType payload_type_g7231= +{ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE( 0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH( 0), + NORMAL_BITRATE( 6300), + MIME_TYPE ("G723") +}; + +PayloadType payload_type_g729={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE( 0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH( 0), + NORMAL_BITRATE( 8000), + MIME_TYPE ("G729") +}; + +PayloadType mpv= +{ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE( 256000), + MIME_TYPE ("MPV") +}; + + +PayloadType h261={ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("H261") +}; + +PayloadType h263={ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(256000), + MIME_TYPE ("H263") +}; + +PayloadType truespeech= +{ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE( 0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH( 0), + NORMAL_BITRATE(8536), + MIME_TYPE ("TSP0") +}; + + +RtpProfile av_profile; + + +void av_profile_init(RtpProfile *profile) +{ + rtp_profile_clear_all(profile); + rtp_profile_set_name(profile,"AV profile"); + rtp_profile_set_payload(profile,0,&pcmu8000); + rtp_profile_set_payload(profile,1,&lpc1016); + rtp_profile_set_payload(profile,3,&gsm); + rtp_profile_set_payload(profile,4,&payload_type_g7231); + rtp_profile_set_payload(profile,8,&pcma8000); + rtp_profile_set_payload(profile,18,&payload_type_g729); + rtp_profile_set_payload(profile,31,&h261); + rtp_profile_set_payload(profile,32,&mpv); + rtp_profile_set_payload(profile,34,&h263); +} + +/* these are extra payload types that can be used dynamically */ +PayloadType lpc1015={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(2400), + MIME_TYPE ("1015") +}; + +PayloadType speex_nb={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(8000), /*not true: 8000 is the minimum*/ + MIME_TYPE ("speex") +}; + +PayloadType speex_wb={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(16000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(28000), + MIME_TYPE ("speex") +}; + +PayloadType payload_type_ilbc={ + TYPE( PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(13300), /* the minimum, with 30ms frames */ + MIME_TYPE ("iLBC"), +}; + +PayloadType amr={ + TYPE(PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(8000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("AMR") +}; + +PayloadType amrwb={ + TYPE(PAYLOAD_AUDIO_PACKETIZED), + CLOCK_RATE(16000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("AMR-WB") +}; + +PayloadType mp4v={ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("MP4V-ES") +}; + + +PayloadType h263_1998={ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("H263-1998") +}; + +PayloadType h263_2000={ + TYPE( PAYLOAD_VIDEO), + CLOCK_RATE(90000), + BITS_PER_SAMPLE(0), + ZERO_PATTERN(NULL), + PATTERN_LENGTH(0), + NORMAL_BITRATE(0), + MIME_TYPE ("H263-2000") +}; diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.c new file mode 100644 index 00000000..0f061c36 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.c @@ -0,0 +1,25 @@ +/* + * file : export.c + * + * this file is used when building the oRTP stack as a dynamic loadable library + * on win32 OS. Indeed, structures cannot been exported 'as is' using the .def + * file. Since we want to use the av_profile and telephone_event instances as defined + * in the original source code, We have to implement those 2 functions to retrieve + * pointers on them. + * + */ + + +#include "export.h" + + +RtpProfile * get_av_profile( void ) +{ + return &av_profile; +} + +PayloadType * get_telephone_event( void ) +{ + return &telephone_event; +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.h new file mode 100644 index 00000000..0f5a3992 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/export.h @@ -0,0 +1,38 @@ +/* + * file : export.h + * + * this file is used when building the oRTP stack as a dynamic loadable library + * on win32 OS. Indeed, structures cannot been exported 'as is' using the .def + * file. Since we want to use the av_profile and telephone_event instances as defined + * in the original source code, We have to implement those 2 functions to retrieve + * pointers on them. + * + */ + + + +#ifndef EXPORT_H +#define EXPORT_H + + +#if defined __cplusplus +extern "C" +{ +#endif + + +#include "payloadtype.h" +#include "telephonyevents.h" + + + RtpProfile * get_av_profile( void ); + PayloadType * get_telephone_event( void ); + + + +#if defined __cplusplus +} +#endif + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.c new file mode 100644 index 00000000..5f236f71 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.c @@ -0,0 +1,141 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*************************************************************************** + * jitterctl.c + * + * Mon Nov 8 11:53:21 2004 + * Copyright 2004 Simon MORLAT + * Email simon.morlat@linphone.org + ****************************************************************************/ + +#include <rtpsession.h> +#include <payloadtype.h> +#include <math.h> +#include <stdlib.h> + +#define JC_BETA 0.03 /*allows a clock slide around 3% */ +#define JC_GAMMA (JC_BETA) + +#include "jitterctl.h" + +void jitter_control_init(JitterControl *ctl, int base_jiitt_time, PayloadType *payload){ + ctl->count=0; + ctl->slide=0; + ctl->jitter=0; + ctl->inter_jitter=0; + ctl->slide=0; + if (base_jiitt_time!=-1) ctl->jitt_comp = base_jiitt_time; + /* convert in timestamp unit: */ + if (payload!=NULL){ + jitter_control_set_payload(ctl,payload); + } + ctl->adapt_jitt_comp_ts=ctl->jitt_comp_ts; + ctl->corrective_slide=0; +} + +void jitter_control_enable_adaptive(JitterControl *ctl, gboolean val){ + ctl->adaptive=val; +} + +void jitter_control_set_payload(JitterControl *ctl, PayloadType *pt){ + ctl->jitt_comp_ts = + (gint) (((double) ctl->jitt_comp / 1000.0) * (pt->clock_rate)); + ctl->corrective_step=(160 * 8000 )/pt->clock_rate; /* This formula got to me after some beers */ + ctl->adapt_jitt_comp_ts=ctl->jitt_comp_ts; +} + + +void jitter_control_dump_stats(JitterControl *ctl){ + g_log("oRTP-stats",G_LOG_LEVEL_MESSAGE,"JitterControl:\n\tslide=%g,jitter=%g,count=%i", + ctl->slide,ctl->jitter, ctl->count); +} + + +/* + The algorithm computes two values: + slide: an average of difference between the expected and the socket-received timestamp + jitter: an average of the absolute value of the difference between socket-received timestamp and slide. + slide is used to make clock-slide detection and correction. + jitter is added to the initial jitt_comp_time value. It compensates bursty packets arrival (packets + not arriving at regular interval ). +*/ +void jitter_control_new_packet(JitterControl *ctl, guint32 packet_ts, guint32 cur_str_ts, gint32 * slide, gint32 *safe_delay){ + int diff=packet_ts - cur_str_ts; + float gap; + int d; + //printf("diff=%g\n",diff); + + ctl->count++; + ctl->slide= (ctl->slide*(1-JC_BETA)) + ((float)diff*JC_BETA); + gap=fabs((float)diff - ctl->slide); + ctl->jitter=(ctl->jitter*(1-JC_GAMMA)) + (gap*JC_GAMMA); + d=diff-ctl->olddiff; + ctl->inter_jitter=ctl->inter_jitter+ (( (float)abs(d) - ctl->inter_jitter)*(1/16.0)); + ctl->olddiff=diff; + if (ctl->adaptive){ + int tmp; + if (ctl->count%50==0) { + /*jitter_control_dump_stats(ctl);*/ + } + tmp=(int)(ctl->slide)-ctl->corrective_slide; + if (tmp>ctl->corrective_step) ctl->corrective_slide+=ctl->corrective_step; + else if (tmp<-ctl->corrective_step) ctl->corrective_slide-=ctl->corrective_step; + /* the following is nearly equivalent, but maybe it consumes more CPU: ?*/ + /*ctl->corrective_slide=(((gint)ctl->slide)/ctl->corrective_step)*ctl->corrective_step;*/ + + ctl->adapt_jitt_comp_ts=MAX(ctl->jitt_comp_ts,ctl->jitter); + + *slide=(gint32)ctl->slide; + *safe_delay=(gint32)ctl->adapt_jitt_comp_ts; + }else { + *slide=0; + *safe_delay=(gint32)ctl->jitt_comp_ts; + } + return ; +} + + +/** + *rtp_session_set_jitter_compensation: + *@session: a RtpSession + *@milisec: the time interval in milisec to be jitter compensed. + * + * Sets the time interval for which packet are buffered instead of being delivered to the + * application. + **/ +void +rtp_session_set_jitter_compensation (RtpSession * session, gint milisec) +{ + PayloadType *payload = rtp_profile_get_payload (session->profile, + session-> + payload_type); + if (payload==NULL){ + g_warning("rtp_session_set_jitter_compensation: cannot set because the payload type is unknown"); + return; + } + jitter_control_init(&session->rtp.jittctl,milisec,payload); +} + +void rtp_session_enable_adaptive_jitter_compensation(RtpSession *session, gboolean val){ + jitter_control_enable_adaptive(&session->rtp.jittctl,val); +} + +gboolean rtp_session_adaptive_jitter_compensation_enabled(RtpSession *session){ + return session->rtp.jittctl.adaptive; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.h new file mode 100644 index 00000000..8e6986a9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/jitterctl.h @@ -0,0 +1,38 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*************************************************************************** + * jitterctl.c + * + * Mon Nov 8 11:53:21 2004 + * Copyright 2004 Simon MORLAT + * Email simon.morlat@linphone.org + ****************************************************************************/ + +#ifndef JITTERCTL_H +#define JITTERCTL_H + + +void jitter_control_init(JitterControl *ctl, int base_jiitt_time, PayloadType *pt); +void jitter_control_enable_adaptive(JitterControl *ctl, gboolean val); +void jitter_control_new_packet(JitterControl *ctl, guint32 packet_ts, guint32 cur_str_ts, + gint32 * slide, gint32 *safe_delay); +#define jitter_control_adaptive_enabled(ctl) ((ctl)->adaptive) +void jitter_control_set_payload(JitterControl *ctl, PayloadType *pt); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp-config.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp-config.h new file mode 100644 index 00000000..049a76d8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp-config.h @@ -0,0 +1,9 @@ +#include <config.h> + +#define ORTP_MAJOR_VERSION 0 +#define ORTP_MINOR_VERSION 7 +#define ORTP_MICRO_VERSION 1 +#define ORTP_EXTRA_VERSION +#define ORTP_VERSION "0.7.1" + +#define POSIXTIMER_INTERVAL 10000 diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.c new file mode 100644 index 00000000..80e07682 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.c @@ -0,0 +1,258 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <ortp.h> +#include "scheduler.h" +#include <stdlib.h> + +rtp_stats_t ortp_global_stats; + +#ifdef ENABLE_MEMCHECK +gint ortp_allocations=0; +#endif + + +RtpScheduler *__ortp_scheduler; + + +void dummy_log(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + return; +} + +extern void av_profile_init(RtpProfile *profile); + +static void init_random_number_generator(){ + struct timeval t; + gettimeofday(&t,NULL); + srandom(t.tv_usec+t.tv_sec); +} + +/** + *ortp_init: + * + * Initialize the oRTP library. You should call this function first before using + * oRTP API. +**/ + +void ortp_init() +{ + static gboolean initialized=FALSE; + if (initialized) return; + initialized=TRUE; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD( 1, 0 ); + + if (WSAStartup(wVersionRequested,&wsaData)!=0) + { + g_error("Fail to initialise socket api"); + } +#endif + +#ifdef HAVE_GLIB + if (!g_thread_supported()) g_thread_init (NULL); +#endif + av_profile_init(&av_profile); + ortp_global_stats_reset(); + init_random_number_generator(); + g_message("oRTP-" ORTP_VERSION "initialized."); +} + + +/** + *ortp_scheduler_init: + * + * Initialize the oRTP scheduler. You only have to do that if you intend to use the + * scheduled mode of the #RtpSession in your application. + * +**/ +void ortp_scheduler_init() +{ + static gboolean initialized=FALSE; + if (initialized) return; + initialized=TRUE; +#ifdef __hpux + /* on hpux, we must block sigalrm on the main process, because signal delivery + is ?random?, well, sometimes the SIGALRM goes to both the main thread and the + scheduler thread */ + sigset_t set; + sigemptyset(&set); + sigaddset(&set,SIGALRM); + sigprocmask(SIG_BLOCK,&set,NULL); +#endif /* __hpux */ + if (!g_thread_supported()) g_thread_init(NULL); + __ortp_scheduler=rtp_scheduler_new(); + rtp_scheduler_start(__ortp_scheduler); + //sleep(1); +} + + +/** + *ortp_exit: + * + * Gracefully uninitialize the library, including shutdowning the scheduler if it was started. + * +**/ +void ortp_exit() +{ + if (__ortp_scheduler!=NULL) + { + rtp_scheduler_destroy(__ortp_scheduler); + __ortp_scheduler=NULL; + } +} + +/** + *ortp_get_scheduler: + * + * Returns a pointer to the scheduler, NULL if it was not running. + * The application developer should have to call this function. + * + *Returns: a pointer to the scheduler. +**/ +RtpScheduler * ortp_get_scheduler() +{ + if (__ortp_scheduler==NULL) g_error("Cannot use the scheduled mode: the scheduler is not " + "started. Call ortp_scheduler_init() at the begginning of the application."); + return __ortp_scheduler; +} + + +void ortp_log(const gchar *log_domain,GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + gchar *lev; + switch(log_level){ + case G_LOG_LEVEL_MESSAGE: + lev="message"; + break; + case G_LOG_LEVEL_WARNING: + lev="warning"; + break; + case G_LOG_LEVEL_ERROR: + lev="error"; + default: + lev="(unknown log type)"; + } + if (user_data==NULL){ + user_data=stdout; + } + fprintf((FILE*)user_data,"%s-%s:%s\n",log_domain,lev,message); +} + + +/** + *ortp_set_debug_file: + *@domain: one of "oRTP" or "oRTP-stats" logging domain. + *@file: a FILE pointer where to output the messages from the domain. + * + * Warning: domain is ignored when not compiling with glib support. +**/ +void ortp_set_debug_file(gchar *domain,FILE *file) +{ + if (file!=NULL) + g_log_set_handler (domain, G_LOG_LEVEL_MASK, ortp_log, (gpointer)file); + else g_log_set_handler (domain, G_LOG_LEVEL_MASK, dummy_log, NULL); +} +/** + *ortp_set_log_handler: + *@domain: one of the "oRTP" or "oRTP-stats" logging domain. + *@func: your logging function, compatible with the GLogFunc prototype. + * + * Warning: domain is ignored when not compiling with glib support. +**/ +void ortp_set_log_handler(const gchar *domain, GLogFunc func, gpointer userdata){ + g_log_set_handler(domain,G_LOG_LEVEL_MASK,func,userdata); +} + + + +void ortp_global_stats_display() +{ + rtp_stats_display(&ortp_global_stats,"Global statistics"); +#ifdef ENABLE_MEMCHECK + printf("Unfreed allocations: %i\n",ortp_allocations); +#endif +} + + +void rtp_stats_display(rtp_stats_t *stats, char *header) +{ + + g_log("oRTP-stats",G_LOG_LEVEL_MESSAGE, + "\n %s :\n" + " number of rtp packet sent=%lld\n" + " number of rtp bytes sent=%lld bytes\n" + " number of rtp packet received=%lld\n" + " number of rtp bytes received=%lld bytes\n" + " number of incoming rtp bytes successfully delivered to the application=%lld \n" + " number of times the application queried a packet that didn't exist=%lld \n" + " number of rtp packets received too late=%lld\n" + " number of rtp packets skipped=%lld\n" + " number of bad formatted rtp packets=%lld\n" + " number of packet discarded because of queue overflow=%lld\n", + header, + (long long)stats->packet_sent, + (long long)stats->sent, + (long long)stats->packet_recv, + (long long)stats->hw_recv, + (long long)stats->recv, + (long long)stats->unavaillable, + (long long)stats->outoftime, + (long long)stats->skipped, + (long long)stats->bad, + (long long)stats->discarded); +} + +void ortp_global_stats_reset(){ + memset(&ortp_global_stats,0,sizeof(rtp_stats_t)); +} + +rtp_stats_t *ortp_get_global_stats(){ + return &ortp_global_stats; +} + +void rtp_stats_reset(rtp_stats_t *stats){ + memset((void*)stats,0,sizeof(rtp_stats_t)); +} + + +/** + *ortp_min_version_required: + *@major: + *@minor: + *@micro: + * + * This function give the opportunity to programs to check if the libortp they link to + * has the minimum version number they need. + * + * Returns: true if ortp has a version number greater or equal than the required one. +**/ +gboolean ortp_min_version_required(int major, int minor, int micro){ + return ((major*1000000) + (minor*1000) + micro) <= + ((ORTP_MAJOR_VERSION*1000000) + (ORTP_MINOR_VERSION*1000) + ORTP_MICRO_VERSION); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.h new file mode 100644 index 00000000..a3c8b01c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/ortp.h @@ -0,0 +1,54 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef ORTP_H +#define ORTP_H + +#include <rtpsession.h> +#include <sessionset.h> + +#ifdef __cplusplus +extern "C" +{ +#endif +gboolean ortp_min_version_required(int major, int minor, int micro); +void ortp_init(); +void ortp_scheduler_init(); +void ortp_exit(); + + +void ortp_set_debug_file(gchar *domain, FILE *file); +/* domain is ignored when not compiling with glib support */ +void ortp_set_log_handler(const gchar *domain, GLogFunc func, gpointer ud); + +extern rtp_stats_t ortp_global_stats; + +void ortp_global_stats_reset(); +rtp_stats_t *ortp_get_global_stats(); + +void ortp_global_stats_display(); +void rtp_stats_display(rtp_stats_t *stats, char *header); +void rtp_stats_reset(rtp_stats_t *stats); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.c new file mode 100644 index 00000000..c4886208 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.c @@ -0,0 +1,268 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <payloadtype.h> + +#include <stdio.h> +#include <stdlib.h> + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +/** + *rtp_profile_clear_all: + *@profile: an RTP profile (#RtpProfile object) + * + * Initialize the profile to the empty profile (all payload type are unassigned). + * +**/ + +/** + *rtp_profile_set_name: + *@profile: a rtp profile object (#RtpProfile) + *@nm: a string + * + * Set a name to the rtp profile. (This is not required) + * +**/ + +/** + *rtp_profile_get_name: + *@profile: a rtp profile object (#RtpProfile) + * + *Returns: the name of the rtp profile. May be NULL. +**/ + +/** + *rtp_profile_set_payload: + *@profile: an RTP profile (a #RtpProfile object) + *@index: the payload type number + *@pt: the payload type description (a #PayloadType object ) + * + * Assign payload type number @index to payload type desribed in @pt for the RTP profile + * @profile. + * +**/ + +/** + *rtp_profile_get_payload: + *@profile: an RTP profile (a #RtpProfile object) + *@index: the payload type number + * + * Gets the payload description of the payload type @index in the profile @profile. + * + *Returns: the payload description (a #PayloadType object) +**/ + +/** + *rtp_profile_clear_payload: + *@profile: an RTP profile (a #RtpProfile object) + *@index: the payload type number + * + * Set payload type number @index unassigned in profile @profile. + * +**/ + +char *payload_type_get_rtpmap(PayloadType *pt) +{ + int len=strlen(pt->mime_type)+15; + char *rtpmap=g_malloc(len); + snprintf(rtpmap,len,"%s/%i/1",pt->mime_type,pt->clock_rate); + return rtpmap; +} + +PayloadType *payload_type_new() +{ + PayloadType *newpayload=g_new0(PayloadType,1); + newpayload->flags|=PAYLOAD_TYPE_ALLOCATED; + return newpayload; +} + + +PayloadType *payload_type_clone(PayloadType *payload) +{ + PayloadType *newpayload=g_new0(PayloadType,1); + memcpy(newpayload,payload,sizeof(PayloadType)); + newpayload->mime_type=g_strdup(payload->mime_type); + if (payload->fmtp!=NULL) newpayload->fmtp=g_strdup(payload->fmtp); + newpayload->flags|=PAYLOAD_TYPE_ALLOCATED; + return newpayload; +} + +void payload_type_destroy(PayloadType *pt) +{ + g_free(pt->mime_type); + g_free(pt->fmtp); + g_free(pt); +} + +gint rtp_profile_get_payload_number_from_mime(RtpProfile *profile,const char *mime) +{ + PayloadType *pt; + gint i; + for (i=0;i<RTP_PROFILE_MAX_PAYLOADS;i++) + { + pt=rtp_profile_get_payload(profile,i); + if (pt!=NULL) + { + if (strcasecmp(pt->mime_type,mime)==0){ + return i; + } + } + } + return -1; +} + +gint rtp_profile_find_payload_number(RtpProfile*profile,const gchar *mime,int rate) +{ + int i; + PayloadType *pt; + for (i=0;i<RTP_PROFILE_MAX_PAYLOADS;i++) + { + pt=rtp_profile_get_payload(profile,i); + if (pt!=NULL) + { + if (strcasecmp(pt->mime_type,mime)==0 && pt->clock_rate==rate){ + + return i; + } + } + } + return -1; +} + +gint rtp_profile_get_payload_number_from_rtpmap(RtpProfile *profile,const char *rtpmap) +{ + gint clock_rate,ret; + char *p,*mime,*tmp,*c; + + /* parse the rtpmap */ + tmp=g_strdup(rtpmap); + p=strchr(tmp,'/'); + if (p!=NULL){ + mime=tmp; + *p='\0'; + c=p+1; + p=strchr(c,'/'); + if (p!=NULL) *p='\0'; + clock_rate=atoi(c); + }else return -1; + + //printf("Searching for payload %s at freq %i",mime,clock_rate); + ret=rtp_profile_find_payload_number(profile,mime,clock_rate); + g_free(tmp); + return ret; +} + +PayloadType * rtp_profile_find_payload(RtpProfile *prof,const gchar *mime,int rate) +{ + int i; + i=rtp_profile_find_payload_number(prof,mime,rate); + if (i>=0) return rtp_profile_get_payload(prof,i); + return NULL; +} + + +PayloadType * rtp_profile_get_payload_from_mime(RtpProfile *profile,const char *mime) +{ + int pt; + pt=rtp_profile_get_payload_number_from_mime(profile,mime); + if (pt==-1) return NULL; + else return rtp_profile_get_payload(profile,pt); +} + + +PayloadType * rtp_profile_get_payload_from_rtpmap(RtpProfile *profile, const char *rtpmap) +{ + int pt; + pt=rtp_profile_get_payload_number_from_rtpmap(profile,rtpmap); + if (pt==-1) return NULL; + else return rtp_profile_get_payload(profile,pt); +} + +int rtp_profile_move_payload(RtpProfile *prof,int oldpos,int newpos){ + prof->payload[newpos]=prof->payload[oldpos]; + prof->payload[oldpos]=NULL; + return 0; +} + +RtpProfile * rtp_profile_new(const char *name) +{ + RtpProfile *prof=g_new0(RtpProfile,1); + rtp_profile_set_name(prof,name); + rtp_profile_clear_all(prof); + return prof; +} + +void rtp_profile_set_name(RtpProfile *obj, const char *name){ + if (obj->name!=NULL) g_free(obj->name); + obj->name=g_strdup(name); +} + +/* ! payload are not cloned*/ +RtpProfile * rtp_profile_clone(RtpProfile *prof) +{ + int i; + PayloadType *pt; + RtpProfile *newprof=rtp_profile_new(prof->name); + rtp_profile_clear_all(newprof); + for (i=0;i<128;i++){ + pt=rtp_profile_get_payload(prof,i); + if (pt!=NULL){ + rtp_profile_set_payload(newprof,i,pt); + } + } + return newprof; +} + +void rtp_profile_copy(const RtpProfile *orig, RtpProfile *dest){ + memcpy(dest,orig,sizeof(RtpProfile)); +} + + +/*clone a profile and its payloads */ +RtpProfile * rtp_profile_clone_full(RtpProfile *prof) +{ + int i; + PayloadType *pt; + RtpProfile *newprof=rtp_profile_new(prof->name); + rtp_profile_clear_all(newprof); + for (i=0;i<RTP_PROFILE_MAX_PAYLOADS;i++){ + pt=rtp_profile_get_payload(prof,i); + if (pt!=NULL){ + rtp_profile_set_payload(newprof,i,payload_type_clone(pt)); + } + } + return newprof; +} + +void rtp_profile_destroy(RtpProfile *prof) +{ + int i; + PayloadType *payload; + for (i=0;i<RTP_PROFILE_MAX_PAYLOADS;i++) + { + payload=rtp_profile_get_payload(prof,i); + if (payload!=NULL && (payload->flags & PAYLOAD_TYPE_ALLOCATED)) + payload_type_destroy(payload); + } + g_free(prof); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.h new file mode 100644 index 00000000..e5efa797 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/payloadtype.h @@ -0,0 +1,136 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PAYLOADTYPE_H +#define PAYLOADTYPE_H +#include <rtpport.h> + +typedef enum{ + PAYLOAD_TYPE_ALLOCATED = 1 +}PayloadTypeFlags; + +struct _PayloadType +{ + gint type; + #define PAYLOAD_AUDIO_CONTINUOUS 0 + #define PAYLOAD_AUDIO_PACKETIZED 1 + #define PAYLOAD_VIDEO 2 + #define PAYLOAD_OTHER 3 /* ?? */ + gint clock_rate; + char bits_per_sample; /* in case of continuous audio data */ + char *zero_pattern; + gint pattern_length; + /* other usefull information */ + gint normal_bitrate; /*in bit/s */ + char *mime_type; + char *fmtp; /*various parameters, as a string */ + PayloadTypeFlags flags; + void *user_data; +}; + +#ifndef PayloadType_defined +#define PayloadType_defined +typedef struct _PayloadType PayloadType; +#endif + +#ifdef __cplusplus +extern "C"{ +#endif +PayloadType *payload_type_new(); +PayloadType *payload_type_clone(PayloadType *payload); +void payload_type_destroy(PayloadType *pt); +#ifdef __cplusplus +} +#endif + +#define payload_type_set_flag(pt,flag) (pt)->flags|=(flag) +#define payload_type_unset_flag(pt,flag) (pt)->flags&=(~flag) + +#define RTP_PROFILE_MAX_PAYLOADS 128 + +struct _RtpProfile +{ + char *name; + PayloadType *payload[RTP_PROFILE_MAX_PAYLOADS]; +}; + + +typedef struct _RtpProfile RtpProfile; + + +extern RtpProfile av_profile; + +#define payload_type_set_user_data(pt,p) (pt)->user_data=(p) +#define payload_type_get_user_data(pt) ((pt)->user_data) + + + +#define rtp_profile_get_name(profile) (const char*)((profile)->name) +#define rtp_profile_set_payload(profile,index,pt) (profile)->payload[(index)]=(pt) +#define rtp_profile_clear_payload(profile,index) (profile)->payload[(index)]=NULL +#define rtp_profile_clear_all(profile) memset((void*)(profile),0,sizeof(RtpProfile)) +#define rtp_profile_get_payload(profile,index) ((profile)->payload[(index)]) +#ifdef __cplusplus +extern "C"{ +#endif +void rtp_profile_set_name(RtpProfile *prof, const char *name); +PayloadType * rtp_profile_get_payload_from_mime(RtpProfile *profile,const char *mime); +PayloadType * rtp_profile_get_payload_from_rtpmap(RtpProfile *profile, const char *rtpmap); +gint rtp_profile_get_payload_number_from_mime(RtpProfile *profile,const char *mime); +gint rtp_profile_get_payload_number_from_rtpmap(RtpProfile *profile, const char *rtpmap); +gint rtp_profile_find_payload_number(RtpProfile *prof,const gchar *mime,int rate); +PayloadType * rtp_profile_find_payload(RtpProfile *prof,const gchar *mime,int rate); +gint rtp_profile_move_payload(RtpProfile *prof,int oldpos,int newpos); + +RtpProfile * rtp_profile_new(const char *name); +/* clone a profile, payload are not cloned */ +RtpProfile * rtp_profile_clone(RtpProfile *prof); +/* copy a profile into another one; payload are not cloned */ +void rtp_profile_copy(const RtpProfile *orig, RtpProfile *dest); + +/*clone a profile and its payloads (ie payload type are newly allocated, not reusing payload types of the reference profile) */ +RtpProfile * rtp_profile_clone_full(RtpProfile *prof); +/* frees the profile and all its PayloadTypes*/ +void rtp_profile_destroy(RtpProfile *prof); +#ifdef __cplusplus +} +#endif + +/* some payload types */ +/* audio */ +extern PayloadType pcmu8000; +extern PayloadType pcma8000; +extern PayloadType pcm8000; +extern PayloadType lpc1016; +extern PayloadType gsm; +extern PayloadType lpc1015; +extern PayloadType speex_nb; +extern PayloadType speex_wb; +extern PayloadType payload_type_ilbc; +extern PayloadType truespeech; + +/* video */ +extern PayloadType mpv; +extern PayloadType h261; +extern PayloadType h263; + + + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.c new file mode 100644 index 00000000..2ad6b4dc --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.c @@ -0,0 +1,183 @@ + +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* port_fct.h. define methods to help for portability between unix and win32 */ + +#include <unistd.h> + +#include <rtpport.h> +#include "port_fct.h" + + + +/* + * this method is an utility method that calls fnctl() on UNIX or + * ioctlsocket on Win32. + * int retrun the result of the system method + */ +int set_non_blocking_socket (gint sock) +{ + + +#ifndef _WIN32 + return fcntl (sock, F_SETFL, O_NONBLOCK); +#else + unsigned long nonBlock = 1; + return ioctlsocket(sock, FIONBIO , &nonBlock); +#endif +} + + +/* + * this method is an utility method that calls close() on UNIX or + * closesocket on Win32. + * int retrun the result of the system method + */ +#ifndef _WIN32 + int close_socket(gint sock) + { + return close (sock); + } +#else + int close_socket(SOCKET sock) + { + return closesocket(sock); + } +#endif + + + +/* + * getSocketError() return a string describing the error + */ +#ifdef _WIN32 +char *getSocketError() +{ + int error = WSAGetLastError (); + static char buf[80]; + + switch (error) + { + case WSANOTINITIALISED: return "Windows sockets not initialized : call WSAStartup"; + case WSAEADDRINUSE: return "Local Address already in use"; + case WSAEADDRNOTAVAIL: return "The specified address is not a valid address for this machine"; +// case WSAEFAULT: return ""; +// case WSAEINPROGRESS: return ""; + case WSAEINVAL: return "The socket is already bound to an address."; + case WSAENOBUFS: return "Not enough buffers available, too many connections."; + case WSAENOTSOCK: return "The descriptor is not a socket."; + case WSAECONNRESET: return "Connection reset by peer"; +/* + + case : return ""; + case : return ""; + case : return ""; + case : return ""; + case : return ""; + case : return ""; +*/ + default : + sprintf (buf,"Error code : %d", error); + return buf; + break; + } + + return buf; + +} +#endif + +#ifndef _WIN32 + /* Use UNIX inet_aton method */ +#else + int inet_aton (const char * cp, struct in_addr * addr) + { + unsigned long retval; + + retval = inet_addr (cp); + + if (retval == INADDR_NONE) + { + return -1; + } + else + { + addr->S_un.S_addr = retval; + return 1; + } + } +#endif + +#ifndef HAVE_GLIB + +char * g_strdup_vprintf(const char *fmt, va_list ap) +{ + /* Guess we need no more than 100 bytes. */ + int n, size = 100; + char *p; + if ((p = g_malloc (size)) == NULL) + return NULL; + while (1) + { + /* Try to print in the allocated space. */ + n = vsnprintf (p, size, fmt, ap); + /* If that worked, return the string. */ + if (n > -1 && n < size) + return p; + //printf("Reallocing space.\n"); + /* Else try again with more space. */ + if (n > -1) /* glibc 2.1 */ + size = n + 1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + if ((p = g_realloc (p, size)) == NULL) + return NULL; + } +} + + + +extern void ortp_log(const gchar *log_domain,GLogLevelFlags log_level, + const gchar *message, + gpointer user_data); + +static GLogFunc __log_func=ortp_log; +static gpointer __log_user_data=(gpointer)NULL; + +void g_log(const gchar *log_domain,GLogLevelFlags log_level,const gchar *format,...){ + va_list args; + va_start(args,format); + g_logv(log_domain,log_level,format,args); + va_end(args); +} + +void g_logv(const gchar *log_domain,GLogLevelFlags log_level,const gchar *format,va_list args){ + gchar *msg; + msg=g_strdup_vprintf(format,args); + __log_func(log_domain,log_level,msg,__log_user_data); + g_free(msg); +} + +void g_log_set_handler(const gchar *log_domain,GLogLevelFlags log_levels, GLogFunc log_func, gpointer user_data){ + __log_func=log_func; + __log_user_data=user_data; +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.h new file mode 100644 index 00000000..3b3abe31 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/port_fct.h @@ -0,0 +1,48 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* fct-win32.h. define unix methods that are not defined in win32 env */ + +#ifndef PORT_FCT_H +#define PORT_FCT_H + +#ifdef _WIN32 +#include <Winsock2.h> +#else +#include <fcntl.h> +#endif + +#include <rtpport.h> + +#ifndef _WIN32 +/* use unix pthread_t... */ + extern int close_socket(gint sock); +#else + #define pthread_t HANDLE + extern int pthread_create(pthread_t *thread,const void *attr,void *(__cdecl *start)(void *),void* arg); + extern int pthread_join(pthread_t thread,void **); + + extern int close_socket(SOCKET sock); + extern int inet_aton (const char * cp, struct in_addr * addr); +#endif + +extern int set_non_blocking_socket (int sock); +extern int set_thread_priority(); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/posixtimer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/posixtimer.c new file mode 100644 index 00000000..9e20ead4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/posixtimer.c @@ -0,0 +1,167 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <rtpport.h> +#include "rtptimer.h" + + +#ifndef _WIN32 + +#ifdef __linux__ +#include <sys/select.h> +#endif + +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + + +static struct timeval orig,cur; +static guint32 posix_timer_time=0; /*in milisecond */ + +void posix_timer_init() +{ + posix_timer.state=RTP_TIMER_RUNNING; + gettimeofday(&orig,NULL); + posix_timer_time=0; +} + + + + +void posix_timer_do() +{ + gint32 diff,time; + struct timeval tv; + + gettimeofday(&cur,NULL); + time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 ); + if ( (diff=time-posix_timer_time)>50){ + g_warning("Must catchup %i miliseconds.",diff); + } + while((diff = posix_timer_time-time) > 0) + { + tv.tv_sec = diff/1000; + tv.tv_usec = (diff%1000)*1000; + select(0,NULL,NULL,NULL,&tv); + gettimeofday(&cur,NULL); + time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 ); + } + posix_timer_time+=POSIXTIMER_INTERVAL/1000; + +} + +void posix_timer_uninit() +{ + posix_timer.state=RTP_TIMER_STOPPED; +} + +RtpTimer posix_timer={ 0, + posix_timer_init, + posix_timer_do, + posix_timer_uninit, + {0,POSIXTIMER_INTERVAL}}; + + +#else //WIN32 + +#include <windows.h> +#include <mmsystem.h> + + +MMRESULT timerId; +HANDLE TimeEvent; +int late_ticks; + + +static DWORD posix_timer_time; +static DWORD offset_time; + + +#define TIME_INTERVAL 50 +#define TIME_RESOLUTION 10 +#define TIME_TIMEOUT 100 + + + +void CALLBACK timerCb(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + // Check timerId + if (timerId == uID) + { + SetEvent(TimeEvent); + posix_timer_time += TIME_INTERVAL; + } +} + + +void win_timer_init(void) +{ + timerId = timeSetEvent(TIME_INTERVAL,10,timerCb,0,TIME_PERIODIC | TIME_CALLBACK_FUNCTION); + TimeEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + + late_ticks = 0; + + offset_time = GetTickCount(); + posix_timer_time=0; +} + + +void win_timer_do(void) +{ + DWORD diff; + + // If timer have expired while we where out of this method + // Try to run after lost time. + if (late_ticks > 0) + { + late_ticks--; + posix_timer_time+=TIME_INTERVAL; + return; + } + + + diff = GetTickCount() - posix_timer_time - offset_time; + if( diff>TIME_INTERVAL && (diff<(1<<31))) + { + late_ticks = diff/TIME_INTERVAL; + g_warning("we must catchup %i ticks.",late_ticks); + return; + } + + WaitForSingleObject(TimeEvent,TIME_TIMEOUT); + return; +} + + +void win_timer_close(void) +{ + timeKillEvent(timerId); +} + +RtpTimer toto; + +RtpTimer posix_timer={ 0, + win_timer_init, + win_timer_do, + win_timer_close, + {0,TIME_INTERVAL * 1000}}; + + +#endif // _WIN32 diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.c new file mode 100644 index 00000000..78599ae5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.c @@ -0,0 +1,294 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*************************************************************************** + * rtcp.c + * + * Wed Dec 1 11:45:30 2004 + * Copyright 2004 Simon Morlat + * Email simon dot morlat at linphone dot org + ****************************************************************************/ + +#include <rtpsession.h> +#include <rtcp.h> + +extern gint ortp_rtcp_send (RtpSession * session, mblk_t * m); + + +void rtcp_common_header_init(rtcp_common_header_t *ch, RtpSession *s,int type, int rc, int bytes_len){ + rtcp_common_header_set_version(ch,2); + rtcp_common_header_set_padbit(ch,0); + rtcp_common_header_set_packet_type(ch,type); + rtcp_common_header_set_rc(ch,rc); /* as we don't yet support multi source receiving */ + rtcp_common_header_set_length(ch,(bytes_len/4)-1); +} + +mblk_t *sdes_chunk_new(guint32 ssrc){ + mblk_t *m=allocb(RTCP_SDES_CHUNK_DEFAULT_SIZE,0); + sdes_chunk_t *sc=(sdes_chunk_t*)m->b_rptr; + sc->csrc=htonl(ssrc); + m->b_wptr+=sizeof(sc->csrc); + return m; +} + + +mblk_t * sdes_chunk_append_item(mblk_t *m, rtcp_sdes_type_t sdes_type, const gchar *content) +{ + if ( content ) + { + sdes_item_t si; + si.item_type=sdes_type; + si.len=MIN(strlen(content),RTCP_SDES_MAX_STRING_SIZE); + m=appendb(m,(char*)&si,RTCP_SDES_ITEM_HEADER_SIZE,FALSE); + m=appendb(m,content,si.len,FALSE); + } + return m; +} + +void sdes_chunk_set_ssrc(mblk_t *m, guint32 ssrc){ + sdes_chunk_t *sc=(sdes_chunk_t*)m->b_rptr; + sc->csrc=htonl(ssrc); +} + +#define sdes_chunk_get_ssrc(m) ntohl(((sdes_chunk_t*)((m)->b_rptr))->csrc) + +mblk_t * sdes_chunk_pad(mblk_t *m){ + return appendb(m,NULL,0,TRUE); +} + +void rtp_session_set_source_description(RtpSession *session, + const gchar *cname, const gchar *name, const gchar *email, const gchar *phone, + const gchar *loc, const gchar *tool, const gchar *note){ + mblk_t *chunk = sdes_chunk_new(session->send_ssrc); + mblk_t *m=chunk; + const gchar *_cname=cname; + if (_cname==NULL) + { + _cname="Unknown"; + } + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_CNAME, _cname); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NAME, name); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_EMAIL, email); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_PHONE, phone); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_LOC, loc); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_TOOL, tool); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NOTE, note); + chunk=sdes_chunk_pad(chunk); + rtp_session_lock(session); + if (session->sd!=NULL) freemsg(session->sd); + session->sd=m; + rtp_session_unlock(session); +} + +void +rtp_session_add_contributing_source(RtpSession *session, guint32 csrc, + const gchar *cname, const gchar *name, const gchar *email, const gchar *phone, + const gchar *loc, const gchar *tool, const gchar *note) +{ + mblk_t *chunk = sdes_chunk_new(csrc); + mblk_t *m=chunk; + gchar *_cname=(gchar*)cname; + if (_cname==NULL) + { + _cname="toto"; + } + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_CNAME, cname); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NAME, name); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_EMAIL, email); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_PHONE, phone); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_LOC, loc); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_TOOL, tool); + chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NOTE, note); + chunk=sdes_chunk_pad(chunk); + rtp_session_lock(session); + putq(&session->contributing_sources,m); + rtp_session_unlock(session); +} + + + +mblk_t* rtp_session_create_rtcp_sdes_packet(RtpSession *session) +{ + mblk_t *mp=allocb(sizeof(rtcp_common_header_t),0); + rtcp_common_header_t *rtcp; + mblk_t *tmp,*m=mp; + queue_t *q; + int rc=0; + rtcp = (rtcp_common_header_t*)mp->b_wptr; + mp->b_wptr+=sizeof(rtcp_common_header_t); + + /* concatenate all sdes chunks */ + sdes_chunk_set_ssrc(session->sd,session->send_ssrc); + m=concatb(m,dupmsg(session->sd)); + rc++; + + q=&session->contributing_sources; + for (tmp=qbegin(q); !qend(q,tmp); tmp=qnext(q,mp)){ + m=concatb(m,dupmsg(tmp)); + rc++; + } + rtcp_common_header_init(rtcp,session,RTCP_SDES,rc,msgdsize(mp)); + return mp; +} + + +mblk_t *rtcp_create_simple_bye_packet(guint32 ssrc, const gchar *reason) +{ + gint strsize=0; + gint packet_size; + mblk_t *mp; + rtcp_bye_t *rtcp; + packet_size = RTCP_BYE_HEADER_SIZE + 1 + strsize; + if (reason!=NULL) + strsize=MIN(strlen(reason),RTCP_BYE_REASON_MAX_STRING_SIZE); + mp = allocb(packet_size, 0); + + rtcp = (rtcp_bye_t*)mp->b_rptr; + + rtcp_common_header_init(&rtcp->ch,NULL,RTCP_BYE,1,packet_size); + rtcp->ssrc[0] = htonl(ssrc); + mp->b_wptr += packet_size; + /* append the reason if any*/ + if (reason!=NULL) + appendb(mp,reason,strsize,FALSE); + return mp; +} + +void rtp_session_remove_contributing_sources(RtpSession *session, guint32 ssrc) +{ + queue_t *q=&session->contributing_sources; + mblk_t *tmp; + for (tmp=qbegin(q); !qend(q,tmp); tmp=qnext(q,tmp)){ + guint32 csrc=sdes_chunk_get_ssrc(tmp); + if (csrc==ssrc) { + remq(q,tmp); + break; + } + } + tmp=rtcp_create_simple_bye_packet(ssrc, NULL); + ortp_rtcp_send(session,tmp); +} + +void sender_info_init(sender_info_t *info, RtpSession *session){ + struct timeval tv; + guint32 tmp; + gettimeofday(&tv,NULL); + info->ntp_timestamp_msw=htonl(tv.tv_sec); + tmp=(guint32)((double)tv.tv_usec*(double)(1LL<<32)*1.0e-6); + info->ntp_timestamp_lsw=htonl(tmp); + info->rtp_timestamp=htonl(session->rtp.snd_last_ts); + info->senders_packet_count=htonl(session->rtp.stats.packet_sent); + info->senders_octet_count=htonl(session->rtp.stats.sent); +} + + + +void report_block_init(report_block_t *b, RtpSession *session){ + guint packet_loss=0; + guint8 loss_fraction=0; + RtpStream *stream=&session->rtp; + guint32 delay_snc_last_sr=0; + + /* compute the statistics */ + /*printf("hwrcv_extseq.one=%u, hwrcv_seq_at_last_SR=%u hwrcv_since_last_SR=%u\n", + stream->hwrcv_extseq.one, + stream->hwrcv_seq_at_last_SR, + stream->hwrcv_since_last_SR + );*/ + if (stream->hwrcv_seq_at_last_SR!=0){ + packet_loss=(stream->hwrcv_extseq.one - stream->hwrcv_seq_at_last_SR) - stream->hwrcv_since_last_SR; + stream->stats.cum_packet_loss+=packet_loss; + loss_fraction=(int)(256.0*(float)packet_loss/(float)stream->hwrcv_since_last_SR); + } + /* reset them */ + stream->hwrcv_since_last_SR=0; + stream->hwrcv_seq_at_last_SR=stream->hwrcv_extseq.one; + + if (stream->last_rcv_SR_time.tv_sec!=0){ + struct timeval now; + gfloat delay; + gettimeofday(&now,NULL); + delay=((now.tv_sec-stream->last_rcv_SR_time.tv_sec)*1e6 ) + (now.tv_usec-stream->last_rcv_SR_time.tv_usec); + delay=delay*65536*1e-6; + delay_snc_last_sr=(guint32) delay; + } + + b->ssrc=htonl(session->recv_ssrc); + b->fraction_lost=loss_fraction; + b->cum_num_packet_lost=hton24(stream->stats.cum_packet_loss); + b->interarrival_jitter=htonl((guint32) stream->jittctl.inter_jitter); + b->ext_high_seq_num_rec=htonl(stream->hwrcv_extseq.one); + b->lsr=htonl(stream->last_rcv_SR_ts); + b->delay_snc_last_sr=htonl(delay_snc_last_sr); +} + + + +int rtcp_sr_init(RtpSession *session, char *buf, int size){ + rtcp_sr_t *sr=(rtcp_sr_t*)buf; + if (size<sizeof(rtcp_sr_t)) return -1; + rtcp_common_header_init(&sr->ch,session,RTCP_SR,1,sizeof(rtcp_sr_t)); + sr->ssrc=htonl(session->send_ssrc); + sender_info_init(&sr->si,session); + report_block_init(&sr->rb[0],session); + return sizeof(rtcp_sr_t); +} + +int rtcp_rr_init(RtpSession *session, char *buf, int size){ + rtcp_rr_t *rr=(rtcp_rr_t*)buf; + if (size<sizeof(rtcp_rr_t)) return -1; + rtcp_common_header_init(&rr->ch,session,RTCP_RR,1,sizeof(rtcp_sr_t)); + rr->ssrc=htonl(session->send_ssrc); + report_block_init(&rr->rb[0],session); + return sizeof(rtcp_sr_t); +} + + +void __rtp_session_rtcp_process(RtpSession *session){ + mblk_t *cm=NULL; + mblk_t *sdes=NULL; + if (session->mode==RTP_SESSION_SENDONLY || session->mode==RTP_SESSION_SENDRECV){ + /* first make a SR packet */ + cm=allocb(sizeof(rtcp_sr_t),0); + cm->b_wptr+=rtcp_sr_init(session,cm->b_wptr,sizeof(rtcp_sr_t)); + /* make a SDES packet */ + sdes=rtp_session_create_rtcp_sdes_packet(session); + /* link them */ + cm->b_cont=sdes; + }else{ + /* make a RR packet */ + cm=allocb(sizeof(rtcp_rr_t),0); + cm->b_wptr+=rtcp_rr_init(session,cm->b_wptr,sizeof(rtcp_rr_t)); + /* if we are recv-only do we need to add SDES packet ? I don't think so + as we are not a source */ + } + /* send the compound packet */ + ortp_rtcp_send(session,cm); + ortp_debug("Rtcp compound message sent."); +} + +void rtp_session_rtcp_process(RtpSession *session){ + RtpStream *st=&session->rtp; + if (st->rcv_last_app_ts - st->last_rtcp_report_snt_r > st->rtcp_report_snt_interval + || st->snd_last_ts - st->last_rtcp_report_snt_s > st->rtcp_report_snt_interval){ + st->last_rtcp_report_snt_r=st->rcv_last_app_ts; + st->last_rtcp_report_snt_s=st->snd_last_ts; + __rtp_session_rtcp_process(session); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.h new file mode 100644 index 00000000..574bcf91 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcp.h @@ -0,0 +1,167 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef RTCP_H +#define RTCP_H + +#include <rtpport.h> + +#define RTCP_MAX_RECV_BUFSIZE 1024 + +#define RTCP_SENDER_INFO_SIZE 20 +#define RTCP_REPORT_BLOCK_SIZE 24 +#define RTCP_COMMON_HEADER_SIZE 4 +#define RTCP_SSRC_FIELD_SIZE 4 + + +/* RTCP common header */ + +typedef enum { + RTCP_SR = 200, + RTCP_RR = 201, + RTCP_SDES = 202, + RTCP_BYE = 203, + RTCP_APP = 204 +} rtcp_type_t; + + +typedef struct rtcp_common_header +{ +#ifdef WORDS_BIGENDIAN + guint16 version:2; + guint16 padbit:1; + guint16 rc:5; + guint16 packet_type:8; +#else + guint16 rc:5; + guint16 padbit:1; + guint16 version:2; + guint16 packet_type:8; +#endif + guint16 length:16; +} rtcp_common_header_t; + +#define rtcp_common_header_set_version(ch,v) (ch)->version=v +#define rtcp_common_header_set_padbit(ch,p) (ch)->padbit=p +#define rtcp_common_header_set_rc(ch,rc) (ch)->rc=rc +#define rtcp_common_header_set_packet_type(ch,pt) (ch)->packet_type=pt +#define rtcp_common_header_set_length(ch,l) (ch)->length=htons(l) + +#define rtcp_common_header_get_version(ch,v) ((ch)->version) +#define rtcp_common_header_get padbit(ch,p) ((ch)->padbit) +#define rtcp_common_header_get_rc(ch,rc) ((ch)->rc) +#define rtcp_common_header_get_packet_type(ch,pt) ((ch)->packet_type) +#define rtcp_common_header_get_length(ch,l) ntohs((ch)->length) + + +/* SR or RR packets */ + +typedef struct sender_info +{ + guint32 ntp_timestamp_msw; + guint32 ntp_timestamp_lsw; + guint32 rtp_timestamp; + guint32 senders_packet_count; + guint32 senders_octet_count; +} sender_info_t; + +typedef struct report_block +{ + guint32 ssrc; + guint32 fraction_lost:8; + guint32 cum_num_packet_lost:24; /*cumulative number of packet lost*/ + guint32 ext_high_seq_num_rec; /*extended highest sequence number received */ + guint32 interarrival_jitter; + guint32 lsr; /*last SR */ + guint32 delay_snc_last_sr; /*delay since last sr*/ +} report_block_t; + + +/* SDES packets */ + +typedef enum { + RTCP_SDES_END = 0, + RTCP_SDES_CNAME = 1, + RTCP_SDES_NAME = 2, + RTCP_SDES_EMAIL = 3, + RTCP_SDES_PHONE = 4, + RTCP_SDES_LOC = 5, + RTCP_SDES_TOOL = 6, + RTCP_SDES_NOTE = 7, + RTCP_SDES_PRIV = 8, + RTCP_SDES_MAX = 9 +} rtcp_sdes_type_t; + +typedef struct sdes_chunk +{ + guint32 csrc; +} sdes_chunk_t; + +typedef struct sdes_item +{ + guint8 item_type; + guint8 len; + gchar content[1]; +} sdes_item_t; + +#define RTCP_SDES_MAX_STRING_SIZE 255 +#define RTCP_SDES_ITEM_HEADER_SIZE 2 +#define RTCP_SDES_CHUNK_DEFAULT_SIZE 1024 +#define RTCP_SDES_CHUNK_HEADER_SIZE (sizeof(sdes_chunk_t)) + +/* RTCP bye packet */ + +typedef struct rtcp_bye_reason +{ + guint8 len; + gchar content[1]; +} rtcp_bye_reason_t; + +typedef struct rtcp_bye +{ + rtcp_common_header_t ch; + guint32 ssrc[1]; /* the bye may contain several ssrc/csrc */ +} rtcp_bye_t; +#define RTCP_BYE_HEADER_SIZE sizeof(rtcp_bye_t) +#define RTCP_BYE_REASON_MAX_STRING_SIZE 255 + +#define rtcp_bye_set_ssrc(b,pos,ssrc) (b)->ssrc[pos]=htonl(ssrc) +#define rtcp_bye_get_ssrc(b,pos) ntohl((b)->ssrc[pos]) + + +typedef struct rtcp_sr{ + rtcp_common_header_t ch; + guint32 ssrc; + sender_info_t si; + report_block_t rb[1]; +} rtcp_sr_t; + +typedef struct rtcp_rr{ + rtcp_common_header_t ch; + guint32 ssrc; + report_block_t rb[1]; +} rtcp_rr_t; + +struct _RtpSession; +void rtp_session_rtcp_process(struct _RtpSession *s); + +#define RTCP_DEFAULT_REPORT_INTERVAL 5 + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcpparse.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcpparse.c new file mode 100644 index 00000000..77e38245 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtcpparse.c @@ -0,0 +1,218 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This source code file was written by Nicola Baldo as an extension of + the oRTP library. Copyright (C) 2005 Nicola Baldo nicola@baldo.biz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <ortp.h> + + + +void report_block_parse(RtpSession *session, report_block_t *rb, struct timeval rcv_time_tv) +{ + rb->ssrc = ntohl(rb->ssrc); + + if ( rb->ssrc != session->send_ssrc ) + + { + ortp_debug("Received rtcp report block related to unknown ssrc (not from us)... discarded"); + return; + } + + else + + { + guint32 rcv_time_msw; + guint32 rcv_time_lsw; + guint32 rcv_time; + double rtt; + + rcv_time_msw = rcv_time_tv.tv_sec; + rcv_time_lsw = (guint32) ((double)rcv_time_tv.tv_usec*(double)(1LL<<32)*1.0e-6); + rcv_time = (rcv_time_msw<<16) | (rcv_time_lsw >> 16); + + rb->cum_num_packet_lost = ntoh24(rb->cum_num_packet_lost); + rb->ext_high_seq_num_rec = ntohl(rb->ext_high_seq_num_rec); + rb->interarrival_jitter = ntohl(rb->interarrival_jitter); + rb->lsr = ntohl(rb->lsr); + rb->delay_snc_last_sr = ntohl(rb->delay_snc_last_sr); + + + /* calculating Round Trip Time*/ + if (rb->lsr != 0) + { + rtt = (double) (rcv_time - rb->delay_snc_last_sr - rb->lsr); + rtt = rtt/65536; + //printf("RTT = %f s\n",rtt); + } + + } + +} + + +void rtcp_parse(RtpSession *session, mblk_t *mp) +{ + rtcp_common_header_t *rtcp; + int msgsize; + int rtcp_pk_size; + RtpStream *rtpstream=&session->rtp; + struct timeval rcv_time_tv; + + + gettimeofday(&rcv_time_tv,NULL); + + g_return_if_fail(mp!=NULL); + + msgsize=mp->b_wptr-mp->b_rptr; + + + + if (msgsize < RTCP_COMMON_HEADER_SIZE) + { + ortp_debug("Receiving too short rtcp packet... discarded"); + return; + } + + rtcp=(rtcp_common_header_t *)mp->b_rptr; + + + /* compound rtcp packet can be composed by more than one rtcp message */ + while (msgsize >= RTCP_COMMON_HEADER_SIZE) + + { + + if (rtcp->version!=2) + { + ortp_debug("Receiving rtcp packet with version number !=2...discarded"); + return; + } + + /* convert header data from network order to host order */ + rtcp->length = ntohs(rtcp->length); + + + switch (rtcp->packet_type) + + { + + case RTCP_SR: + + { + rtcp_sr_t *sr = (rtcp_sr_t *) rtcp; + report_block_t *rb; + int i; + + if ( ntohl(sr->ssrc) != session->recv_ssrc ) + { + ortp_debug("Receiving rtcp sr packet from unknown ssrc.. discarded"); + return; + } + + if (msgsize < RTCP_COMMON_HEADER_SIZE + RTCP_SSRC_FIELD_SIZE + RTCP_SENDER_INFO_SIZE + (RTCP_REPORT_BLOCK_SIZE*sr->ch.rc)) + { + ortp_debug("Receiving too short rtcp sr packet... discarded"); + return; + } + + /* parsing RTCP Sender Info */ + sr->si.ntp_timestamp_msw = ntohl(sr->si.ntp_timestamp_msw); + sr->si.ntp_timestamp_lsw = ntohl(sr->si.ntp_timestamp_lsw); + sr->si.rtp_timestamp = ntohl(sr->si.rtp_timestamp); + sr->si.senders_packet_count = ntohl(sr->si.senders_packet_count); + sr->si.senders_octet_count = ntohl(sr->si.senders_octet_count); + + /* saving data to fill LSR and DLSR field in next RTCP report to be transmitted */ + rtpstream->last_rcv_SR_ts = (sr->si.ntp_timestamp_msw << 16) | (sr->si.ntp_timestamp_lsw >> 16); + rtpstream->last_rcv_SR_time.tv_usec = rcv_time_tv.tv_usec; + rtpstream->last_rcv_SR_time.tv_sec = rcv_time_tv.tv_sec; + + + /* parsing all RTCP report blocks */ + for (i=0; i<sr->ch.rc; i++) + { + rb = &(sr->rb[i]); + report_block_parse(session, rb, rcv_time_tv); + } + + } + break; + + + + case RTCP_RR: + + { + rtcp_rr_t *rr = (rtcp_rr_t *) rtcp; + report_block_t *rb; + int i; + + if ( ntohl(rr->ssrc) != session->recv_ssrc ) + { + ortp_debug("Receiving rtcp rr packet from unknown ssrc.. discarded"); + return; + } + + if (msgsize < RTCP_COMMON_HEADER_SIZE + RTCP_SSRC_FIELD_SIZE + (RTCP_REPORT_BLOCK_SIZE*rr->ch.rc)) + { + ortp_debug("Receiving too short rtcp sr packet... discarded"); + return; + } + + /* parsing all RTCP report blocks */ + for (i=0; i<rr->ch.rc; i++) + { + rb = &(rr->rb[i]); + report_block_parse(session, rb, rcv_time_tv); + } + + } + break; + + + case RTCP_SDES: + /* to be implemented */ + break; + + + case RTCP_BYE: + /* to be implemented */ + break; + + + case RTCP_APP: + /* to be implemented */ + break; + + + default: + + ortp_debug("Receiving unknown rtcp packet type... discarded"); + return; + + } + + + rtcp_pk_size = ((rtcp->length)+1)*4; /* current RTCP packet size, in octets */ + msgsize -= rtcp_pk_size; /* size of unparsed portion of UDP packet, in octets */ + rtcp = (rtcp_common_header_t *) (rtcp_pk_size + (char *) rtcp); /* pointer to next RTCP packet in current UDP packet */ + + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtp.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtp.h new file mode 100644 index 00000000..625d2111 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtp.h @@ -0,0 +1,90 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef RTP_H +#define RTP_H + +#include <rtpport.h> + + + + +#define RTP_MAX_RQ_SIZE 100 /* max number of packet allowed to be enqueued */ + +#define IPMAXLEN 20 +#define UDP_MAX_SIZE 65536 +#define RTP_FIXED_HEADER_SIZE 12 +#define RTP_DEFAULT_JITTER_TIME 80 /*miliseconds*/ + + + +typedef struct rtp_header +{ +#ifdef WORDS_BIGENDIAN + guint16 version:2; + guint16 padbit:1; + guint16 extbit:1; + guint16 cc:4; + guint16 markbit:1; + guint16 paytype:7; +#else + guint16 cc:4; + guint16 extbit:1; + guint16 padbit:1; + guint16 version:2; + guint16 paytype:7; + guint16 markbit:1; +#endif + guint16 seq_number; + guint32 timestamp; + guint32 ssrc; + guint32 csrc[16]; +} rtp_header_t; + + + + +typedef struct rtp_stats +{ + guint64 packet_sent; + guint64 sent; /* bytes sent */ + guint64 recv; /* bytes received and delivered in time to the application */ + guint64 hw_recv; /* bytes of udp packets received */ + guint64 packet_recv; /* number of packets received */ + guint64 unavaillable; /* packets not availlable when they were queried */ + guint64 outoftime; /* number of packets that were received too late */ + guint64 skipped; /* number of packets skipped (that the application never queried + or that need to be skipped in order to compensate a clock slide (see adaptive jitter control)) */ + guint64 cum_packet_loss; /* cumulative number of packet lost */ + guint64 bad; /* packets that did not appear to be RTP */ + guint64 discarded; /* incoming packets discarded because the queue exceeds its max size */ +} rtp_stats_t; + +#define RTP_TIMESTAMP_IS_NEWER_THAN(ts1,ts2) \ + ((guint32)((guint32)(ts1) - (guint32)(ts2))< (guint32)(1<<31)) + +#define RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(ts1,ts2) \ + ( ((guint32)((guint32)(ts1) - (guint32)(ts2))< (guint32)(1<<31)) && (ts1)!=(ts2) ) + +#define TIME_IS_NEWER_THAN(t1,t2) RTP_TIMESTAMP_IS_NEWER_THAN(t1,t2) + +#define TIME_IS_STRICTLY_NEWER_THAN(t1,t2) RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(t1,t2) + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.c new file mode 100644 index 00000000..50aeef10 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.c @@ -0,0 +1,122 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + + +#include <rtp.h> +#include "rtpmod.h" + +#define RTP_SEQ_IS_GREATER(seq1,seq2)\ + ((guint16)((guint16)(seq1) - (guint16)(seq2))< (guint16)(1<<15)) + +/* put an rtp packet in queue. It is called by rtp_parse()*/ +void rtp_putq(queue_t *q, mblk_t *mp) +{ + mblk_t *tmp; + rtp_header_t *rtp=(rtp_header_t*)mp->b_rptr,*tmprtp; + /* insert message block by increasing time stamp order : the last (at the bottom) + message of the queue is the newest*/ + ortp_debug("rtp_putq(): Enqueuing packet with ts=%i and seq=%i",rtp->timestamp,rtp->seq_number); + + if (qempty(q)) { + putq(q,mp); + return; + } + tmp=qlast(q); + /* we look at the queue from bottom to top, because enqueued packets have a better chance + to be enqueued at the bottom, since there are surely newer */ + while (!qend(q,tmp)) + { + tmprtp=(rtp_header_t*)tmp->b_rptr; + ortp_debug("rtp_putq(): Seeing packet with seq=%i",tmprtp->seq_number); + + if (rtp->seq_number == tmprtp->seq_number) + { + /* this is a duplicated packet. Don't queue it */ + ortp_debug("rtp_putq: duplicated message."); + freemsg(mp); + return; + }else if (RTP_SEQ_IS_GREATER(rtp->seq_number,tmprtp->seq_number)){ + + insq(q,tmp->b_next,mp); + return; + } + tmp=tmp->b_prev; + } + /* this packet is the oldest, it has to be + placed on top of the queue */ + insq(q,qfirst(q),mp); + +} + + + +mblk_t *rtp_getq(queue_t *q,guint32 timestamp, int *rejected) +{ + mblk_t *tmp,*ret=NULL,*old; + rtp_header_t *tmprtp; + guint32 oldest; + guint32 ts_found=0; + + *rejected=0; + ortp_debug("rtp_getq(): Timestamp %i wanted.",timestamp); + + if (qempty(q)) + { + /*ortp_debug("rtp_getq: q is empty.");*/ + return NULL; + } + /* prevent somebody to ask for a timestamp that is older than the oldest of the queue */ + oldest=((rtp_header_t*) qfirst(q)->b_rptr)->timestamp; + if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(oldest,timestamp)) + { + ortp_debug("rtp_getq(): asking for too old packet ! oldest=%i",oldest); + return NULL; + } + ret=NULL; + old=NULL; + /* return the packet with ts just equal or older than the asked timestamp */ + while ((tmp=qfirst(q))!=NULL) + { + tmprtp=(rtp_header_t*)tmp->b_rptr; + ortp_debug("rtp_getq: Seeing packet with ts=%i",tmprtp->timestamp); + if ( RTP_TIMESTAMP_IS_NEWER_THAN(timestamp,tmprtp->timestamp) ) + { + if (ret!=NULL && tmprtp->timestamp==ts_found) { + /* we've found two packets with same timestamp. return the first one */ + break; + } + if (old!=NULL) { + ortp_debug("rtp_getq: discarding too old packet with ts=%i",ts_found); + (*rejected)++; + freemsg(old); + } + ret=getq(q); /* dequeue the packet, since it has an interesting timestamp*/ + ts_found=tmprtp->timestamp; + ortp_debug("rtp_getq: Found packet with ts=%i",tmprtp->timestamp); + old=ret; + } + else + { + break; + } + } + return ret; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.h new file mode 100644 index 00000000..e675394d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpmod.h @@ -0,0 +1,31 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef RTPMOD_H +#define RTPMOD_H + +#include <rtpport.h> +#include <str_utils.h> +#include <rtp.h> + +void rtp_putq(queue_t *q, mblk_t *mp); +mblk_t *rtp_getq(queue_t *q,guint32 timestamp, int *rejected); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpparse.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpparse.c new file mode 100644 index 00000000..50ecba97 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpparse.c @@ -0,0 +1,159 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <ortp.h> +#include "rtpmod.h" +#include "jitterctl.h" + + +void split_and_queue(queue_t *q, int maxrqsz, mblk_t *mp, rtp_header_t *rtp, int *discarded) +{ + mblk_t *mdata,*tmp; + int header_size; + *discarded=0; + header_size=RTP_FIXED_HEADER_SIZE+ (4*rtp->cc); + if ((mp->b_wptr - mp->b_rptr)==header_size){ + ortp_debug("Rtp packet contains no data."); + (*discarded)++; + freemsg(mp); + return; + } + /* creates a new mblk_t to be linked with the rtp header*/ + mdata=dupb(mp); + + mp->b_wptr=mp->b_rptr+header_size; + mdata->b_rptr+=header_size; + /* link proto with data */ + mp->b_cont=mdata; + /* and then add the packet to the queue */ + + rtp_putq(q,mp); + /* make some checks: q size must not exceed RtpStream::max_rq_size */ + while (q->q_mcount > maxrqsz) + { + /* remove the oldest mblk_t */ + tmp=getq(q); + if (mp!=NULL) + { + ortp_debug("rtp_putq: Queue is full. Discarding message with ts=%i",((rtp_header_t*)mp->b_rptr)->timestamp); + freemsg(tmp); + (*discarded)++; + } + } +} + +void rtp_parse(RtpSession *session, mblk_t *mp, guint32 local_str_ts) +{ + gint i; + rtp_header_t *rtp; + int msgsize; + RtpStream *rtpstream=&session->rtp; + rtp_stats_t *stats=&rtpstream->stats; + + g_return_if_fail(mp!=NULL); + + msgsize=msgdsize(mp); + ortp_global_stats.hw_recv+=msgsize; + stats->hw_recv+=msgsize; + ortp_global_stats.packet_recv++; + stats->packet_recv++; + + session->rtp.hwrcv_since_last_SR++; + + rtp=(rtp_header_t*)mp->b_rptr; + if (rtp->version!=2) + { + ortp_debug("Receiving rtp packet with version number !=2...discarded"); + stats->bad++; + ortp_global_stats.bad++; + freemsg(mp); + return; + } + + /* convert all header data from network order to host order */ + rtp->seq_number=ntohs(rtp->seq_number); + rtp->timestamp=ntohl(rtp->timestamp); + rtp->ssrc=ntohl(rtp->ssrc); + /* convert csrc if necessary */ + if (rtp->cc*sizeof(guint32) > (msgsize-RTP_FIXED_HEADER_SIZE)){ + ortp_debug("Receiving too short rtp packet."); + stats->bad++; + ortp_global_stats.bad++; + freemsg(mp); + return; + } + for (i=0;i<rtp->cc;i++) + rtp->csrc[i]=ntohl(rtp->csrc[i]); + if (session->recv_ssrc!=0) + { + /*the ssrc is set, so we must check it */ + if (session->recv_ssrc!=rtp->ssrc){ + /*ortp_debug("rtp_parse: bad ssrc - %i",rtp->ssrc);*/ + session->recv_ssrc=rtp->ssrc; + rtp_signal_table_emit(&session->on_ssrc_changed); + } + }else session->recv_ssrc=rtp->ssrc; + + /* update some statistics */ + if (rtp->seq_number>rtpstream->hwrcv_extseq.split.lo){ + rtpstream->hwrcv_extseq.split.lo=rtp->seq_number; + }else if (rtp->seq_number<200 && rtpstream->hwrcv_extseq.split.lo>((1<<16) - 200)){ + /* this is a check for sequence number looping */ + rtpstream->hwrcv_extseq.split.lo=rtp->seq_number; + rtpstream->hwrcv_extseq.split.hi++; + } + + + /* check for possible telephone events */ + if (rtp->paytype==session->telephone_events_pt){ + split_and_queue(&session->rtp.tev_rq,session->rtp.max_rq_size,mp,rtp,&i); + stats->discarded+=i; + ortp_global_stats.discarded+=i; + return; + } + + if (!(session->flags & RTP_SESSION_RECV_SYNC)){ + gint32 slide=0; + gint32 safe_delay=0; + jitter_control_new_packet(&session->rtp.jittctl,rtp->timestamp,local_str_ts,&slide,&safe_delay); + + session->rtp.rcv_diff_ts=session->rtp.hwrcv_diff_ts + slide - safe_delay; + ortp_debug(" rcv_diff_ts=%i", session->rtp.rcv_diff_ts); + + /* detect timestamp important jumps in the future, to workaround stupid rtp senders */ + if (RTP_TIMESTAMP_IS_NEWER_THAN(rtp->timestamp,session->rtp.rcv_last_ts+session->rtp.ts_jump)){ + ortp_debug("rtp_parse: timestamp jump ?"); + rtp_signal_table_emit2(&session->on_timestamp_jump,&rtp->timestamp); + } + else if (RTP_TIMESTAMP_IS_NEWER_THAN(session->rtp.rcv_last_ts,rtp->timestamp)){ + /* avoid very old packet to enqueued, because the user is no more supposed to get them */ + ortp_debug("rtp_parse: silently discarding very old packet (ts=%i)",rtp->timestamp); + freemsg(mp); + stats->outoftime++; + ortp_global_stats.outoftime++; + return; + } + + } + + split_and_queue(&session->rtp.rq,session->rtp.max_rq_size,mp,rtp,&i); + stats->discarded+=i; + ortp_global_stats.discarded+=i; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpport.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpport.h new file mode 100644 index 00000000..65e48530 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpport.h @@ -0,0 +1,308 @@ +/* + The oRTP LinPhone RTP library intends to provide basics for a RTP stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* this file is responsible of the portability of the stack */ + +#ifndef RTPPORT_H +#define RTPPORT_H + +#ifdef UGLIB_H +#define HAVE_GLIB +#endif + +#ifndef _WIN32 /* do not include ortp-config.h when we are on win32 */ +# ifdef _ORTP_SOURCE +# include <ortp-config.h> +# else +# include <ortp-config.h> +# endif +#else + #include "ortp-config-win32.h" +#endif + +#define INT_TO_POINTER(truc) ((gpointer)(long)(truc)) +#define POINTER_TO_INT(truc) ((int)(long)(truc)) + +/* defines things that should be defined when we have not glib */ +#ifndef HAVE_GLIB + +#include <errno.h> +#include <sys/types.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +/* integer types */ +typedef uint64_t guint64; +typedef uint16_t guint16; +typedef uint32_t guint32; +typedef signed short gint16; +typedef int32_t gint32; +typedef unsigned int guint; +typedef int gint; +typedef char gchar; +typedef unsigned char guchar; +typedef unsigned char guint8; +typedef void* gpointer; +typedef int gboolean; +typedef double gdouble; +typedef float gfloat; + +#define TRUE 1 +#define FALSE 0 + + +/*misc*/ +#define g_return_if_fail(expr) if (!(expr)) {printf("%s:%i- assertion" #expr "failed\n",__FILE__,__LINE__); return;} +#define g_return_val_if_fail(expr,ret) if (!(expr)) {printf("%s:%i- assertion" #expr "failed\n",__FILE__,__LINE__); return (ret);} + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> + +typedef enum { + /* GLib log levels */ + G_LOG_LEVEL_ERROR = 1 << 2, /* always fatal */ + G_LOG_LEVEL_CRITICAL = 1 << 3, + G_LOG_LEVEL_WARNING = 1 << 4, + G_LOG_LEVEL_MESSAGE = 1 << 5, + G_LOG_LEVEL_MASK = ~0 + +} GLogLevelFlags; + +#ifndef G_LOG_DOMAIN +#define G_LOG_DOMAIN ((const gchar*)"") +#endif +#ifdef __cplusplus +extern "C" { +#endif + +void g_log(const gchar *log_domain,GLogLevelFlags log_level,const gchar *format,...); +void g_logv(const gchar *log_domain,GLogLevelFlags log_level,const gchar *format,va_list args); +typedef void (*GLogFunc) (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data); +static inline void g_warning(const gchar *fmt,...) +{ + va_list args; + va_start (args, fmt); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args); + va_end (args); +} +static inline void g_error(const gchar *fmt,...) +{ + va_list args; + va_start (args, fmt); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, fmt, args); + va_end (args); +} +static inline void g_message(const gchar *fmt,...) +{ + va_list args; + va_start (args, fmt); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args); + va_end (args); +} +/* in order to simplify, domain is ignored when using the folowing function, ie all logs will go through your handler +whatever the domain is */ +void g_log_set_handler(const gchar *domain, GLogLevelFlags levels, GLogFunc func, gpointer ud); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_GLIB */ + + +#if defined(TIME_WITH_SYS_TIME) +#include <time.h> +#include <sys/time.h> +#elif defined(HAVE_SYS_TIME_H) +#include <sys/time.h> +#endif + + +#ifdef HAVE_GLIB + +#ifndef UGLIB_H +#include <glib.h> +#endif +#include <string.h> + + +#else /* not HAVE_GLIB */ +/* things that in glib, but should only be defined when we are not in the HPUX kernel. */ +#include <stdlib.h> +#include <pthread.h> +#include <sched.h> +#include <string.h> + +#ifdef ENABLE_MEMCHECK +extern gint ortp_allocations; +#endif + +/* memory allocation functions */ +static inline void * g_malloc(int sz) +{ + void *p=malloc(sz); + if (p==NULL) { + printf("g_malloc: Failed to allocate %i bytes: %s.\n",sz,strerror(errno)); + abort(); + } +#ifdef ENABLE_MEMCHECK + ortp_allocations++; +#endif + return p; +} + +static inline void * g_malloc0(int sz) +{ + void *p=malloc(sz); + if (p==NULL) { + printf("g_malloc: Failed to allocate %i bytes: %s.\n",sz,strerror(errno)); + abort(); + } + memset(p,0,sz); +#ifdef ENABLE_MEMCHECK + ortp_allocations++; +#endif + return p; +} + +#define g_new(type,count) (type *)g_malloc(sizeof(type)*(count)) +#define g_new0(type, count) (type *)g_malloc0(sizeof(type)*(count)) +#define g_realloc(p,sz) realloc((p),(sz)) +static inline void g_free(void *p) +{ +#ifdef ENABLE_MEMCHECK + ortp_allocations--; +#endif + free(p); +} + +#define g_strdup(machin) strdup(machin) + +typedef pthread_mutex_t GMutex; +static inline GMutex * g_mutex_new() +{ + pthread_mutex_t *mutex=g_new(pthread_mutex_t,1); + pthread_mutex_init(mutex,NULL); + return mutex; +} +typedef enum +{ + G_THREAD_PRIORITY_LOW, + G_THREAD_PRIORITY_NORMAL, + G_THREAD_PRIORITY_HIGH, + G_THREAD_PRIORITY_URGENT +} GThreadPriority; +typedef pthread_t GThread; +typedef gpointer (*GThreadFunc)(gpointer data); +static inline GThread *g_thread_create(GThreadFunc func, gpointer data, gboolean joinable, void **error){ + GThread *thread=g_new(GThread,1); + pthread_create(thread,NULL,func,data); + return thread; +} + +static inline void g_thread_join(GThread *thread){ + pthread_join(*thread,NULL); + g_free(thread); +} + +static inline void g_thread_set_priority(GThread *thread,GThreadPriority prio){ + if (prio>G_THREAD_PRIORITY_NORMAL){ + /* this is unsupported on HPUX */ + /* + struct sched_param param; + param.sched_priority=1; + sched_setscheduler(*thread,SCHED_RR,¶m); + */ + } +} + +#define g_mutex_lock(mutex) pthread_mutex_lock((mutex)) +#define g_mutex_unlock(mutex) pthread_mutex_unlock((mutex)) +#define g_mutex_free(mutex) pthread_mutex_destroy((mutex));g_free((mutex)) + +typedef pthread_cond_t GCond; +static inline GCond * g_cond_new() +{ + pthread_cond_t *cond=g_new(pthread_cond_t,1); + pthread_cond_init(cond,NULL); + return cond; +} +#define g_cond_wait(cond,mutex) pthread_cond_wait((cond),(mutex)) +#define g_cond_signal(cond) pthread_cond_signal((cond)) +#define g_cond_broadcast(cond) pthread_cond_broadcast((cond)) +#define g_cond_free(cond) pthread_cond_destroy((cond)); g_free((cond)) + +#define g_thread_init(vtable) +#define g_thread_supported() (1) + +#endif /* HAVE_GLIB */ + + +#ifndef RTP_DEBUG +#define ortp_debug(...) +#else +#define ortp_debug g_message +#endif + +#ifdef _WIN32 +extern char *getSocketError(); +#define getSocketErrorCode() WSAGetLastError () +#else +#define getSocketError() strerror(errno) +#define getSocketErrorCode() (errno) +#endif + +#ifdef UGLIB_H +#undef HAVE_GLIB +#endif + +#undef MIN +#define MIN(a,b) (((a)>(b)) ? (b) : (a)) +#undef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) + +typedef struct _dwsplit_t{ +#ifdef WORDS_BIGENDIAN + guint16 hi; + guint16 lo; +#else + guint16 lo; + guint16 hi; +#endif +} dwsplit_t; + +typedef union{ + dwsplit_t split; + guint32 one; +} poly32_t; + +#ifdef WORDS_BIGENDIAN +#define hton24(x) (x) +#else +#define hton24(x) ((( (x) & 0x00ff0000) >>16) | (( (x) & 0x000000ff) <<16) | ( (x) & 0x0000ff00) ) +#endif +#define ntoh24(x) hton24(x) + +#endif /*RTPPORT_H*/ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.c new file mode 100644 index 00000000..de6b2e7d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.c @@ -0,0 +1,1954 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <ortp.h> +#include <telephonyevents.h> +#include "rtpmod.h" +#include "jitterctl.h" +#include "scheduler.h" +#include "port_fct.h" +#include "utils.h" + +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#ifndef _WIN32 +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef INET6 +# include <netdb.h> +# endif +#else +# include <winsock2.h> +# include "errno-win32.h" +#endif + + +#if defined(HAVE_POLL_H) +#include <poll.h> +#elif defined(HAVE_SYS_POLL_H) +#include <sys/poll.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#define USE_SENDMSG 1 +#endif + + + +void wait_point_init(WaitPoint *wp){ + wp->lock=g_mutex_new(); + wp->cond=g_cond_new(); + wp->time=0; + wp->wakeup=FALSE; +} +void wait_point_uninit(WaitPoint *wp){ + g_cond_free(wp->cond); + g_mutex_free(wp->lock); +} + +#define wait_point_lock(wp) g_mutex_lock((wp)->lock) +#define wait_point_unlock(wp) g_mutex_unlock((wp)->lock) + +void wait_point_wakeup_at(WaitPoint *wp, guint32 t, gboolean dosleep){ + wp->time=t; + wp->wakeup=TRUE; + if (dosleep) g_cond_wait(wp->cond,wp->lock); +} + + +gboolean wait_point_check(WaitPoint *wp, guint32 t){ + gboolean ok=FALSE; + + if (wp->wakeup){ + if (TIME_IS_NEWER_THAN(t,wp->time)){ + wp->wakeup=FALSE; + ok=TRUE; + + } + } + return ok; +} +#define wait_point_wakeup(wp) g_cond_signal((wp)->cond); + +extern void rtp_parse(RtpSession *session, mblk_t *mp, guint32 local_str_ts); + + +static guint32 guint32_random(){ + return random(); +} + +void +rtp_session_init (RtpSession * session, gint mode) +{ + memset (session, 0, sizeof (RtpSession)); + session->lock = g_mutex_new (); + session->rtp.max_rq_size = RTP_MAX_RQ_SIZE; + session->mode = mode; + if ((mode == RTP_SESSION_RECVONLY) || (mode == RTP_SESSION_SENDRECV)) + { + rtp_session_set_flag (session, RTP_SESSION_RECV_SYNC); + rtp_session_set_flag (session, RTP_SESSION_RECV_NOT_STARTED); + + } + if ((mode == RTP_SESSION_SENDONLY) || (mode == RTP_SESSION_SENDRECV)) + { + rtp_session_set_flag (session, RTP_SESSION_SEND_NOT_STARTED); + rtp_session_set_flag (session, RTP_SESSION_SEND_SYNC); + session->send_ssrc=guint32_random(); + /* set default source description */ + rtp_session_set_source_description(session,"unknown@unknown",NULL,NULL, + NULL,NULL,"oRTP-" ORTP_VERSION,"This is free sofware (LGPL) !"); + } + session->telephone_events_pt=-1; /* not defined a priori */ + rtp_session_set_profile (session, &av_profile); /*the default profile to work with */ + session->payload_type=0;/* default to something */ + qinit(&session->rtp.rq); + qinit(&session->rtp.tev_rq); + qinit(&session->contributing_sources); + /* init signal tables */ + rtp_signal_table_init (&session->on_ssrc_changed, session,"ssrc_changed"); + rtp_signal_table_init (&session->on_payload_type_changed, session,"payload_type_changed"); + rtp_signal_table_init (&session->on_telephone_event, session,"telephone-event"); + rtp_signal_table_init (&session->on_telephone_event_packet, session,"telephone-event_packet"); + rtp_signal_table_init (&session->on_timestamp_jump,session,"timestamp_jump"); + rtp_signal_table_init (&session->on_network_error,session,"network_error"); + wait_point_init(&session->send_wp); + wait_point_init(&session->recv_wp); + rtp_session_set_jitter_compensation(session,RTP_DEFAULT_JITTER_TIME); + rtp_session_enable_adaptive_jitter_compensation(session,FALSE); + rtp_session_set_time_jump_limit(session,5000); + session->max_buf_size = UDP_MAX_SIZE; +} + +/** + *rtp_session_new: + *@mode: One of the #RtpSessionMode flags. + * + * Creates a new rtp session. + * If the session is able to send data (RTP_SESSION_SENDONLY or RTP_SESSION_SENDRECV), then a + * random SSRC number is choosed for the outgoing stream. + * + *Returns: the newly created rtp session. +**/ + +RtpSession * +rtp_session_new (gint mode) +{ + RtpSession *session; + session = g_malloc (sizeof (RtpSession)); + rtp_session_init (session, mode); + return session; +} + +/** + *rtp_session_set_scheduling_mode: + *@session: a rtp session. + *@yesno: a boolean to indicate the scheduling mode. + * + * Sets the scheduling mode of the rtp session. If @yesno is TRUE, the rtp session is in + * the scheduled mode, that means that you can use session_set_select() to block until it's time + * to receive or send on this session according to the timestamp passed to the respective functions. + * You can also use blocking mode (see rtp_session_set_blocking_mode() ), to simply block within + * the receive and send functions. + * If @yesno is FALSE, the ortp scheduler will not manage those sessions, meaning that blocking mode + * and the use of session_set_select() for this session are disabled. + * +**/ + +void +rtp_session_set_scheduling_mode (RtpSession * session, gint yesno) +{ + if (yesno) + { + RtpScheduler *sched; + sched = ortp_get_scheduler (); + if (sched != NULL) + { + rtp_session_set_flag (session, RTP_SESSION_SCHEDULED); + session->sched = sched; + rtp_scheduler_add_session (sched, session); + } + else + g_warning + ("rtp_session_set_scheduling_mode: Cannot use scheduled mode because the " + "scheduler is not started. Use ortp_scheduler_init() before."); + } + else + rtp_session_unset_flag (session, RTP_SESSION_SCHEDULED); +} + + +/** + *rtp_session_set_blocking_mode: + *@session: a rtp session + *@yesno: a boolean + * + * Using this function implies that you previously enabled scheduled mode on the session + * (see rtp_session_set_scheduling_mode() ). + * rtp_session_set_blocking_mode() defines the behaviour of the rtp_session_recv_with_ts() and + * rtp_session_send_with_ts() functions. If @yesno is TRUE, rtp_session_recv_with_ts() + * will block until it is time for the packet to be received, according to the timestamp + * passed to the function. After this time, the function returns. + * For rtp_session_send_with_ts(), it will block until it is time for the packet to be sent. + * If @yesno is FALSE, then the two functions will return immediately. + * +**/ +void +rtp_session_set_blocking_mode (RtpSession * session, gint yesno) +{ + if (yesno) + rtp_session_set_flag (session, RTP_SESSION_BLOCKING_MODE); + else + rtp_session_unset_flag (session, RTP_SESSION_BLOCKING_MODE); +} + +/** + *rtp_session_set_profile: + *@session: a rtp session + *@profile: a rtp profile + * + * Set the RTP profile to be used for the session. By default, all session are created by + * rtp_session_new() are initialized with the AV profile, as defined in RFC 1890. The application + * can set any other profile instead using that function. + * + * +**/ + +void +rtp_session_set_profile (RtpSession * session, RtpProfile * profile) +{ + session->profile = profile; + rtp_session_telephone_events_supported(session); +} + + +/** + *rtp_session_signal_connect: + *@session: a rtp session + *@signal: the name of a signal + *@cb: a #RtpCallback + *@user_data: a pointer to any data to be passed when invoking the callback. + * + * This function provides the way for an application to be informed of various events that + * may occur during a rtp session. @signal is a string identifying the event, and @cb is + * a user supplied function in charge of processing it. The application can register + * several callbacks for the same signal, in the limit of #RTP_CALLBACK_TABLE_MAX_ENTRIES. + * Here are name and meaning of supported signals types: + * + * "ssrc_changed" : the SSRC of the incoming stream has changed. + * + * "payload_type_changed" : the payload type of the incoming stream has changed. + * + * "telephone-event_packet" : a telephone-event rtp packet (RFC2833) is received. + * + * "telephone-event" : a telephone event has occured. This is a high-level shortcut for "telephone-event_packet". + * + * "network_error" : a network error happened on a socket. Arguments of the callback functions are + * a const char * explaining the error, an int errno error code and the user_data as usual. + * + * "timestamp_jump" : we have received a packet with timestamp in far future compared to last timestamp received. + * The farness of far future is set by rtp_sesssion_set_time_jump_limit() + * + * Returns: 0 on success, -EOPNOTSUPP if the signal does not exists, -1 if no more callbacks + * can be assigned to the signal type. +**/ +int +rtp_session_signal_connect (RtpSession * session, const char *signal, + RtpCallback cb, gpointer user_data) +{ + OList *elem; + for (elem=session->signal_tables;elem!=NULL;elem=o_list_next(elem)){ + RtpSignalTable *s=(RtpSignalTable*) elem->data; + if (strcmp(signal,s->signal_name)==0){ + return rtp_signal_table_add(s,cb,user_data); + } + } + g_warning ("rtp_session_signal_connect: inexistant signal %s",signal); + return -1; +} + + +/** + *rtp_session_signal_disconnect_by_callback: + *@session: a rtp session + *@signal: a signal name + *@cb: a callback function. + * + * Removes callback function @cb to the list of callbacks for signal @signal. + * + *Returns: 0 on success, -ENOENT if the callbacks was not found. +**/ + +int +rtp_session_signal_disconnect_by_callback (RtpSession * session, const gchar *signal, + RtpCallback cb) +{ + OList *elem; + for (elem=session->signal_tables;elem!=NULL;elem=o_list_next(elem)){ + RtpSignalTable *s=(RtpSignalTable*) elem->data; + if (strcmp(signal,s->signal_name)==0){ + return rtp_signal_table_remove_by_callback(s,cb); + } + } + g_warning ("rtp_session_signal_connect: inexistant signal %s",signal); + return -1; +} + +/** + *rtp_session_set_local_addr: + *@session: a rtp session freshly created. + *@addr: a local IP address in the xxx.xxx.xxx.xxx form. + *@port: a local port. + * + * Specify the local addr to be use to listen for rtp packets or to send rtp packet from. + * In case where the rtp session is send-only, then it is not required to call this function: + * when calling rtp_session_set_remote_addr(), if no local address has been set, then the + * default INADRR_ANY (0.0.0.0) IP address with a random port will be used. Calling + * rtp_sesession_set_local_addr() is mandatory when the session is recv-only or duplex. + * + * Returns: 0 on success. +**/ + +gint +rtp_session_set_local_addr (RtpSession * session, const gchar * addr, gint port) +{ + gint err; + gint optval = 1; +#ifdef INET6 + char num[8]; + struct addrinfo hints, *res0, *res; +#endif + + if (session->rtp.socket>0) { + /* dont try to rebind, close socket before */ + close_socket(session->rtp.socket); + close_socket(session->rtcp.socket); + session->rtp.socket=0; + session->rtcp.socket=0; + } + +#ifdef INET6 + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + snprintf(num, sizeof(num), "%d",port); + err = getaddrinfo(addr,num, &hints, &res0); + if (err!=0) { + g_warning ("Error: %s", gai_strerror(err)); + return err; + } + + for (res = res0; res; res = res->ai_next) { + session->rtp.socket = socket(res->ai_family, res->ai_socktype, 0); + if (session->rtp.socket < 0) + continue; + + err = setsockopt (session->rtp.socket, SOL_SOCKET, SO_REUSEADDR, + (void*)&optval, sizeof (optval)); + if (err < 0) + { + g_warning ("Fail to set rtp address reusable: %s.", getSocketError()); + } + + session->rtp.socktype=res->ai_family; + memcpy(&session->rtp.loc_addr, res->ai_addr, res->ai_addrlen); + err = bind (session->rtp.socket, res->ai_addr, res->ai_addrlen); + if (err != 0) + { + g_warning ("Fail to bind rtp socket to port %i: %s.", port, getSocketError()); + close_socket (session->rtp.socket); + continue; + } +#ifndef __hpux + switch (res->ai_family) + { + case AF_INET: + if (IN_MULTICAST(ntohl(((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr))) + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + err = setsockopt(session->rtp.socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err < 0) + { + g_warning ("Fail to join address group: %s.", getSocketError()); + close_socket (session->rtp.socket); + continue; + } + } + break; + case AF_INET6: + if (IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr))) + { + struct ipv6_mreq mreq; + mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + mreq.ipv6mr_interface = 0; + err = setsockopt(session->rtp.socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + if (err < 0) + { + g_warning ("Fail to join address group: %s.", getSocketError()); + close_socket (session->rtp.socket); + continue; + } + } + break; + } +#endif + break; + } + freeaddrinfo(res0); + if (session->rtp.socket < 0){ + if (session->mode==RTP_SESSION_RECVONLY) g_warning("Could not create rtp socket with address %s: %s",addr,getSocketError()); + return -1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + snprintf(num, sizeof(num), "%d", (port + 1)); + + err = getaddrinfo(addr, num, &hints, &res0); + if (err!=0) { + g_warning ("Error: %s", gai_strerror(err)); + return err; + } + + for (res = res0; res; res = res->ai_next) { + session->rtcp.socket = socket(res->ai_family, res->ai_socktype, 0); + + if (session->rtcp.socket < 0) + continue; + + err = setsockopt (session->rtcp.socket, SOL_SOCKET, SO_REUSEADDR, + (void*)&optval, sizeof (optval)); + if (err < 0) + { + g_warning ("Fail to set rtcp address reusable: %s.",getSocketError()); + } + session->rtcp.socktype=res->ai_family; + memcpy( &session->rtcp.loc_addr, res->ai_addr, res->ai_addrlen); + err = bind (session->rtcp.socket, res->ai_addr, res->ai_addrlen); + if (err != 0) + { + g_warning ("Fail to bind rtp socket to port %i: %s.", port, getSocketError()); + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + continue; + } +#ifndef __hpux + switch (res->ai_family) + { + case AF_INET: + if (IN_MULTICAST(ntohl(((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr))) + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + err = setsockopt(session->rtcp.socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err < 0) + { + g_warning ("Fail to join address group: %s.", getSocketError()); + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + continue; + } + } + break; + case AF_INET6: + if (IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr))) + { + struct ipv6_mreq mreq; + mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + mreq.ipv6mr_interface = 0; + err = setsockopt(session->rtcp.socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + if (err < 0) + { + g_warning ("Fail to join address group: %s.", getSocketError()); + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + continue; + } + } + break; + } +#endif + + break; + } + freeaddrinfo(res0); + if (session->rtp.socket < 0){ + g_warning("Could not create rtcp socket with address %s: %s",addr,getSocketError()); + return -1; + } +#else + session->rtp.loc_addr.sin_family = AF_INET; + + err = inet_aton (addr, &session->rtp.loc_addr.sin_addr); + + if (err < 0) + { + g_warning ("Error in socket address:%s.", getSocketError()); + return err; + } + session->rtp.loc_addr.sin_port = htons (port); + + session->rtp.socket = socket (PF_INET, SOCK_DGRAM, 0); + g_return_val_if_fail (session->rtp.socket > 0, -1); + + err = setsockopt (session->rtp.socket, SOL_SOCKET, SO_REUSEADDR, + (void*)&optval, sizeof (optval)); + if (err < 0) + { + g_warning ("Fail to set rtp address reusable: %s.",getSocketError()); + } + + err = bind (session->rtp.socket, + (struct sockaddr *) &session->rtp.loc_addr, + sizeof (struct sockaddr_in)); + + if (err != 0) + { + g_warning ("Fail to bind rtp socket to port %i: %s.", port, getSocketError()); + close_socket (session->rtp.socket); + return -1; + } + memcpy (&session->rtcp.loc_addr, &session->rtp.loc_addr, + sizeof (struct sockaddr_in)); + session->rtcp.loc_addr.sin_port = htons (port + 1); + session->rtcp.socket = socket (PF_INET, SOCK_DGRAM, 0); + g_return_val_if_fail (session->rtcp.socket > 0, -1); + + err = setsockopt (session->rtcp.socket, SOL_SOCKET, SO_REUSEADDR, + (void*)&optval, sizeof (optval)); + if (err < 0) + { + g_warning ("Fail to set rtcp address reusable: %s.",getSocketError()); + } + + err = bind (session->rtcp.socket, + (struct sockaddr *) &session->rtcp.loc_addr, + sizeof (struct sockaddr_in)); + if (err != 0) + { + g_warning ("Fail to bind rtcp socket to port %i: %s.", port + 1, getSocketError()); + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + return -1; + } +#ifndef __hpux + if (IN_MULTICAST(ntohl(session->rtp.loc_addr.sin_addr.s_addr))) + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = session->rtp.loc_addr.sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + err = setsockopt(session->rtp.socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err == 0) + err = setsockopt(session->rtcp.socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err < 0) + { + g_warning ("Fail to join address group: %s.", getSocketError()); + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + return -1; + } + } +#endif +#endif + /* set RTP socket options */ + set_non_blocking_socket (session->rtp.socket); + /* set RTCP socket options */ + set_non_blocking_socket (session->rtcp.socket); + return 0; +} + + +/** + *rtp_session_set_remote_addr: + *@session: a rtp session freshly created. + *@addr: a local IP address in the xxx.xxx.xxx.xxx form. + *@port: a local port. + * + * Sets the remote address of the rtp session, ie the destination address where rtp packet + * are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP + * packets. Rtp packets that don't come from addr:port are discarded. + * + * Returns: 0 on success. +**/ + +gint +rtp_session_set_remote_addr (RtpSession * session, const gchar * addr, gint port) +{ + gint err; +#ifdef INET6 + struct addrinfo hints, *res0, *res; + char num[8]; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + snprintf(num, sizeof(num), "%d", port); + err = getaddrinfo(addr, num, &hints, &res0); + if (err) { + g_warning ("Error in socket address: %s", gai_strerror(err)); + return err; + } +#endif + + if (session->rtp.socket == 0) + { + int retry; + /* the session has not its socket bound, do it */ + g_message ("Setting random local addresses."); + for (retry=0;retry<10;retry++) + { + int localport; + do + { + localport = (rand () + 5000) & 0xfffe; + } + while ((localport < 5000) || (localport > 0xffff)); +#ifdef INET6 + /* bind to an address type that matches the destination address */ + if (res0->ai_addr->sa_family==AF_INET6) + err = rtp_session_set_local_addr (session, "::", localport); + else err=rtp_session_set_local_addr (session, "0.0.0.0", localport); +#else + err = rtp_session_set_local_addr (session, "0.0.0.0", localport); +#endif + + if (err == 0) + break; + } + if (retry == 10){ + g_warning("rtp_session_set_remote_addr: Could not find a random local address for socket !"); + return -1; + } + } + + +#ifdef INET6 + err=1; + for (res = res0; res; res = res->ai_next) { + /* set a destination address that has the same type as the local address */ + if (res->ai_family==session->rtp.socktype ) { + memcpy( &session->rtp.rem_addr, res->ai_addr, res->ai_addrlen); + session->rtp.addrlen=res->ai_addrlen; + err=0; + break; + } + } + freeaddrinfo(res0); + if (err) { + g_warning("Could not set destination for RTP socket to %s:%i.",addr,port); + return -1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + snprintf(num, sizeof(num), "%d", (port + 1)); + err = getaddrinfo(addr, num, &hints, &res0); + if (err) { + g_warning ("Error: %s", gai_strerror(err)); + return err; + } + err=1; + for (res = res0; res; res = res->ai_next) { + /* set a destination address that has the same type as the local address */ + if (res->ai_family==session->rtp.socktype ) { + err=0; + memcpy( &session->rtcp.rem_addr, res->ai_addr, res->ai_addrlen); + session->rtcp.addrlen=res->ai_addrlen; + break; + } + } + freeaddrinfo(res0); + if (err) { + g_warning("Could not set destination for RCTP socket to %s:%i.",addr,port+1); + return -1; + } +#else + session->rtp.addrlen=sizeof(session->rtp.rem_addr); + session->rtp.rem_addr.sin_family = AF_INET; + + err = inet_aton (addr, &session->rtp.rem_addr.sin_addr); + if (err < 0) + { + g_warning ("Error in socket address:%s.", getSocketError()); + return err; + } + session->rtp.rem_addr.sin_port = htons (port); + + memcpy (&session->rtcp.rem_addr, &session->rtp.rem_addr, + sizeof (struct sockaddr_in)); + session->rtcp.rem_addr.sin_port = htons (port + 1); + session->rtcp.addrlen=sizeof(session->rtp.rem_addr); +#endif +#ifndef NOCONNECT + if (session->mode == RTP_SESSION_SENDONLY) + { + err = connect (session->rtp.socket, + (struct sockaddr *) &session->rtp.rem_addr, +#ifdef INET6 + session->rtp.addrlen); +#else + sizeof (struct sockaddr_in)); +#endif + if (err != 0) + { + g_message ("Can't connect rtp socket: %s.",getSocketError()); + return err; + } + err = connect (session->rtcp.socket, + (struct sockaddr *) &session->rtcp.rem_addr, +#ifdef INET6 + session->rtcp.addrlen); +#else + sizeof (struct sockaddr_in)); +#endif + if (err != 0) + { + g_message ("Can't connect rtp socket: %s.",getSocketError()); + return err; + } + } +#endif + return 0; +} + +void rtp_session_set_sockets(RtpSession *session, gint rtpfd, gint rtcpfd) +{ + if (rtpfd>0) set_non_blocking_socket(rtpfd); + if (rtcpfd>0) set_non_blocking_socket(rtcpfd); + session->rtp.socket=rtpfd; + session->rtcp.socket=rtcpfd; + session->flags|=RTP_SESSION_USING_EXT_SOCKETS; +} + +/** + *rtp_session_flush_sockets: + *@session: a rtp session + * + * Flushes the sockets for all pending incoming packets. + * This can be usefull if you did not listen to the stream for a while + * and wishes to start to receive again. During the time no receive is made + * packets get bufferised into the internal kernel socket structure. + * +**/ +void rtp_session_flush_sockets(RtpSession *session){ + char trash[4096]; +#ifdef INET6 + struct sockaddr_storage from; +#else + struct sockaddr from; +#endif + socklen_t fromlen=sizeof(from); + if (session->rtp.socket>0){ + while (recvfrom(session->rtp.socket,(void*)trash,sizeof(trash),0,(struct sockaddr *)&from,&fromlen)>0){}; + } + if (session->rtcp.socket>0){ + while (recvfrom(session->rtcp.socket,(void*)trash,sizeof(trash),0,(struct sockaddr*)&from,&fromlen)>0){}; + } +} + +/** + *rtp_session_set_seq_number: + *@session: a rtp session freshly created. + *@addr: a 16 bit unsigned number. + * + * sets the initial sequence number of a sending session. + * +**/ +void rtp_session_set_seq_number(RtpSession *session, guint16 seq){ + session->rtp.snd_seq=seq; +} + + +guint16 rtp_session_get_seq_number(RtpSession *session){ + return session->rtp.snd_seq; +} + + +#ifdef USE_SENDMSG +#define MAX_IOV 10 +static gint rtp_sendmsg(int sock,mblk_t *m, struct sockaddr *rem_addr, int addr_len){ + int error; + struct msghdr msg; + struct iovec iov[MAX_IOV]; + int iovlen; + for(iovlen=0; iovlen<MAX_IOV && m!=NULL; m=m->b_cont,iovlen++){ + iov[iovlen].iov_base=m->b_rptr; + iov[iovlen].iov_len=m->b_wptr-m->b_rptr; + } + msg.msg_name=(void*)rem_addr; + msg.msg_namelen=addr_len; + msg.msg_iov=&iov[0]; + msg.msg_iovlen=iovlen; + msg.msg_control=NULL; + msg.msg_controllen=0; + msg.msg_flags=0; + + error=sendmsg(sock,&msg,0); + return error; +} +#endif + +static gint +ortp_rtp_send (RtpSession * session, mblk_t * m) +{ + gint error; + int i; + rtp_header_t *hdr; + + hdr = (rtp_header_t *) m->b_rptr; + /* perform host to network conversions */ + hdr->ssrc = htonl (hdr->ssrc); + hdr->timestamp = htonl (hdr->timestamp); + hdr->seq_number = htons (hdr->seq_number); + for (i = 0; i < hdr->cc; i++) + hdr->csrc[i] = htonl (hdr->csrc[i]); + +#ifdef USE_SENDMSG + if (session->flags & RTP_SESSION_USING_EXT_SOCKETS){ + error=rtp_sendmsg(session->rtp.socket,m,(struct sockaddr *)NULL,0); + }else { + error=rtp_sendmsg(session->rtp.socket,m,(struct sockaddr *) &session->rtp.rem_addr, + session->rtp.addrlen); + } +#else + if (m->b_cont!=NULL){ + mblk_t *newm=msgpullup(m,-1); + freemsg(m); + m=newm; + } + if (session->flags & RTP_SESSION_USING_EXT_SOCKETS){ + error=send(session->rtp.socket, m->b_rptr, (m->b_wptr - m->b_rptr),0); + }else error = sendto (session->rtp.socket, m->b_rptr, + (m->b_wptr - m->b_rptr), 0, + (struct sockaddr *) &session->rtp.rem_addr, + session->rtp.addrlen); +#endif + if (error < 0){ + if (session->on_network_error.count>0){ + rtp_signal_table_emit3(&session->on_network_error,(gpointer)"Error sending RTP packet",INT_TO_POINTER(getSocketErrorCode())); + }else g_warning ("Error sending rtp packet: %s ; socket=%i", getSocketError(), session->rtp.socket); + } + freemsg (m); + return error; +} + +gint +ortp_rtcp_send (RtpSession * session, mblk_t * m) +{ + gint error=0; + gboolean using_ext_socket=(session->flags & RTP_SESSION_USING_EXT_SOCKETS)!=0; + if ( (using_ext_socket && session->rtcp.socket>0 ) || session->rtcp.addrlen>0){ + +#ifndef USE_SENDMSG + if (m->b_cont!=NULL){ + mblk_t *newm=msgpullup(m,-1); + freemsg(m); + m=newm; + } +#endif + if (using_ext_socket && session->rtcp.socket>0 ){ +#ifdef USE_SENDMSG + error=rtp_sendmsg(session->rtcp.socket,m,(struct sockaddr *)NULL,0); +#else + error=send(session->rtcp.socket, m->b_rptr, (m->b_wptr - m->b_rptr),0); +#endif + }else { +#ifdef USE_SENDMSG + error=rtp_sendmsg(session->rtcp.socket,m,(struct sockaddr *) &session->rtcp.rem_addr, + session->rtcp.addrlen); +#else + error = sendto (session->rtcp.socket, m->b_rptr, + (m->b_wptr - m->b_rptr), 0, + (struct sockaddr *) &session->rtcp.rem_addr, + session->rtcp.addrlen); +#endif + } + + if (error < 0){ + if (session->on_network_error.count>0){ + rtp_signal_table_emit3(&session->on_network_error,(gpointer)"Error sending RTCP packet",INT_TO_POINTER(getSocketErrorCode())); + }else g_warning ("Error sending rtcp packet: %s ; socket=%i", getSocketError(), session->rtcp.socket); + } + }else g_warning("Cannot send rtcp report because I don't know the remote address."); + freemsg (m); + return error; +} + + +/** + *rtp_session_set_ssrc: + *@session: a rtp session. + *@ssrc: an unsigned 32bit integer representing the synchronisation source identifier (SSRC). + * + * Sets the SSRC for the outgoing stream. + * If not done, a random ssrc is used. + * +**/ +void +rtp_session_set_ssrc (RtpSession * session, guint32 ssrc) +{ + session->send_ssrc = ssrc; +} + +/* this function initialize all session parameter's that depend on the payload type */ +static void payload_type_changed(RtpSession *session, PayloadType *pt){ + jitter_control_set_payload(&session->rtp.jittctl,pt); + session->rtp.rtcp_report_snt_interval=RTCP_DEFAULT_REPORT_INTERVAL*pt->clock_rate; + rtp_session_set_time_jump_limit(session,session->rtp.time_jump); +} + +/** + *rtp_session_set_payload_type: + *@session: a rtp session + *@paytype: the payload type + * + * Sets the payload type of the rtp session. It decides of the payload types written in the + * of the rtp header for the outgoing stream, if the session is SENDRECV or SENDONLY. + * For the incoming stream, it sets the waited payload type. If that value does not match + * at any time this waited value, then the application can be informed by registering + * for the "payload_type_changed" signal, so that it can make the necessary changes + * on the downstream decoder that deals with the payload of the packets. + * + *Returns: 0 on success, -1 if the payload is not defined. +**/ + +int +rtp_session_set_payload_type (RtpSession * session, int paytype) +{ + PayloadType *pt; + session->payload_type = paytype; + pt=rtp_profile_get_payload(session->profile,paytype); + if (pt!=NULL){ + payload_type_changed(session,pt); + } + return 0; +} + +int rtp_session_get_payload_type(RtpSession *session){ + return session->payload_type; +} + + +/** + *rtp_session_set_payload_type_with_string: + *@session: a rtp session + *@paytype: the payload type + * + * Sets the payload type of the rtp session. It decides of the payload types written in the + * of the rtp header for the outgoing stream, if the session is SENDRECV or SENDONLY. + * Unlike #rtp_session_set_payload_type(), it takes as argument a string referencing the + * payload type (mime type). + * For the incoming stream, it sets the waited payload type. If that value does not match + * at any time this waited value, then the application can be informed by registering + * for the "payload_type_changed" signal, so that it can make the necessary changes + * on the downstream decoder that deals with the payload of the packets. + * + *Returns: 0 on success, -1 if the payload is not defined. +**/ + +int +rtp_session_set_payload_type_with_string (RtpSession * session, const char * mime) +{ + int pt; + pt=rtp_profile_get_payload_number_from_mime(session->profile,mime); + if (pt<0) { + g_warning("%s is not a know mime string within the rtpsession's profile.",mime); + return -1; + } + rtp_session_set_payload_type(session,pt); + return 0; +} + + +/** + *rtp_session_create_packet: + *@session: a rtp session. + *@header_size: the rtp header size. For standart size (without extensions), it is #RTP_FIXED_HEADER_SIZE + *@payload :data to be copied into the rtp packet. + *@payload_size : size of data carried by the rtp packet. + * + * Allocates a new rtp packet. In the header, ssrc and payload_type according to the session's + * context. Timestamp and seq number are not set, there will be set when the packet is going to be + * sent with rtp_session_sendm_with_ts(). + * + *Returns: a rtp packet in a mblk_t (message block) structure. +**/ +mblk_t * rtp_session_create_packet(RtpSession *session,gint header_size, const char *payload, gint payload_size) +{ + mblk_t *mp; + gint msglen=header_size+payload_size; + rtp_header_t *rtp; + + mp=allocb(msglen,BPRI_MED); + rtp=(rtp_header_t*)mp->b_rptr; + rtp->version = 2; + rtp->padbit = 0; + rtp->extbit = 0; + rtp->markbit= 0; + rtp->cc = 0; + rtp->paytype = session->payload_type; + rtp->ssrc = session->send_ssrc; + rtp->timestamp = 0; /* set later, when packet is sended */ + rtp->seq_number = 0; /*set later, when packet is sended */ + /*copy the payload */ + mp->b_wptr+=header_size; + memcpy(mp->b_wptr,payload,payload_size); + mp->b_wptr+=payload_size; + return mp; +} + +/** + *rtp_session_create_packet_with_data: + *@session: a rtp session. + *@payload : the data to be sent with this packet + *@payload_size : size of data + *@freefn : a function that will be called when the payload buffer is no more needed. + * + * Creates a new rtp packet using the given payload buffer (no copy). The header will be allocated separetely. + * In the header, ssrc and payload_type according to the session's + * context. Timestamp and seq number are not set, there will be set when the packet is going to be + * sent with rtp_session_sendm_with_ts(). + * oRTP will send this packet using libc's sendmsg() (if this function is availlable!) so that there will be no + * packet concatenation involving copies to be done in user-space. + * @freefn can be NULL, in that case payload will be kept untouched. + * + *Returns: a rtp packet in a mblk_t (message block) structure. +**/ + +mblk_t * rtp_session_create_packet_with_data(RtpSession *session, char *payload, gint payload_size, void (*freefn)(void*)) +{ + mblk_t *mp,*mpayload; + gint header_size=RTP_FIXED_HEADER_SIZE; /* revisit when support for csrc is done */ + rtp_header_t *rtp; + + mp=allocb(header_size,BPRI_MED); + rtp=(rtp_header_t*)mp->b_rptr; + rtp->version = 2; + rtp->padbit = 0; + rtp->extbit = 0; + rtp->markbit= 0; + rtp->cc = 0; + rtp->paytype = session->payload_type; + rtp->ssrc = session->send_ssrc; + rtp->timestamp = 0; /* set later, when packet is sended */ + rtp->seq_number = 0; /*set later, when packet is sended */ + mp->b_wptr+=header_size; + /* create a mblk_t around the user supplied payload buffer */ + mpayload=allocb_with_buf(payload,payload_size,BPRI_MED,freefn); + mpayload->b_wptr+=payload_size; + /* link it with the header */ + mp->b_cont=mpayload; + return mp; +} + + +/** + *rtp_session_create_packet_in_place: + *@session: a rtp session. + *@buffer: a buffer that contains first just enough place to write a RTP header, then the data to send. + *@size : the size of the buffer + *@freefn : a function that will be called once the buffer is no more needed (the data has been sent). + * + * Creates a new rtp packet using the buffer given in arguments (no copy). + * In the header, ssrc and payload_type according to the session's + * context. Timestamp and seq number are not set, there will be set when the packet is going to be + * sent with rtp_session_sendm_with_ts(). + * @freefn can be NULL, in that case payload will be kept untouched. + * + *Returns: a rtp packet in a mblk_t (message block) structure. +**/ +mblk_t * rtp_session_create_packet_in_place(RtpSession *session,char *buffer, gint size, void (*freefn)(void*) ) +{ + mblk_t *mp; + rtp_header_t *rtp; + + mp=allocb_with_buf(buffer,size,BPRI_MED,freefn); + + rtp=(rtp_header_t*)mp->b_rptr; + rtp->version = 2; + rtp->padbit = 0; + rtp->extbit = 0; + rtp->markbit= 0; + rtp->cc = 0; + rtp->paytype = session->payload_type; + rtp->ssrc = session->send_ssrc; + rtp->timestamp = 0; /* set later, when packet is sended */ + rtp->seq_number = 0; /*set later, when packet is sended */ + return mp; +} + + +/** + *rtp_session_sendm_with_ts: + *@session : a rtp session. + *@mp : a rtp packet presented as a mblk_t. + *@timestamp: the timestamp of the data to be sent. Refer to the rfc to know what it is. + * + * Send the rtp datagram @mp to the destination set by rtp_session_set_remote_addr() + * with timestamp @timestamp. For audio data, the timestamp is the number + * of the first sample resulting of the data transmitted. See rfc1889 for details. + * The packet (@mp) is freed once it is sended. + * + *Returns: the number of bytes sent over the network. +**/ +gint +rtp_session_sendm_with_ts (RtpSession * session, mblk_t *mp, guint32 timestamp) +{ + rtp_header_t *rtp; + guint32 packet_time; + gint error = 0; + gint payloadsize; + RtpScheduler *sched=session->sched; + RtpStream *stream=&session->rtp; + + if (session->flags & RTP_SESSION_SEND_NOT_STARTED) + { + session->rtp.snd_ts_offset = timestamp; + if (session->flags & RTP_SESSION_SCHEDULED) + { + session->rtp.snd_time_offset = sched->time_; + } + rtp_session_unset_flag (session,RTP_SESSION_SEND_NOT_STARTED); + } + /* if we are in blocking mode, then suspend the process until the scheduler it's time to send the + * next packet */ + /* if the timestamp of the packet queued is older than current time, then you we must + * not block */ + if (session->flags & RTP_SESSION_SCHEDULED) + { + packet_time = + rtp_session_ts_to_time (session, + timestamp - + session->rtp.snd_ts_offset) + + session->rtp.snd_time_offset; + /*g_message("rtp_session_send_with_ts: packet_time=%i time=%i",packet_time,sched->time_);*/ + wait_point_lock(&session->send_wp); + if (TIME_IS_STRICTLY_NEWER_THAN (packet_time, sched->time_)) + { + wait_point_wakeup_at(&session->send_wp,packet_time,(session->flags & RTP_SESSION_BLOCKING_MODE)!=0); + session_set_clr(&sched->w_sessions,session); /* the session has written */ + } + else session_set_set(&sched->w_sessions,session); /*to indicate select to return immediately */ + wait_point_unlock(&session->send_wp); + } + + + rtp=(rtp_header_t*)mp->b_rptr; + + payloadsize = msgdsize(mp) - RTP_FIXED_HEADER_SIZE - (rtp->cc*sizeof(guint32)); + rtp_session_lock (session); + + /* set a seq number */ + rtp->seq_number=session->rtp.snd_seq; + rtp->timestamp=timestamp; + session->rtp.snd_seq++; + session->rtp.snd_last_ts = timestamp; + + + ortp_global_stats.sent += payloadsize; + stream->stats.sent += payloadsize; + ortp_global_stats.packet_sent++; + stream->stats.packet_sent++; + + error = ortp_rtp_send (session, mp); + rtp_session_rtcp_process(session); + rtp_session_unlock (session); + + return error; +} + + +/** + *rtp_session_send_with_ts: + *@session: a rtp session. + *@buffer: a buffer containing the data to be sent in a rtp packet. + *@len: the length of the data buffer, in bytes. + *@userts: the timestamp of the data to be sent. Refer to the rfc to know what it is. + * + * Send a rtp datagram to the destination set by rtp_session_set_remote_addr() containing + * the data from @buffer with timestamp @userts. This is a high level function that uses + * rtp_session_create_packet() and rtp_session_sendm_with_ts() to send the data. + * + * + *Returns: the number of bytes sent over the network. +**/ +gint +rtp_session_send_with_ts (RtpSession * session, const gchar * buffer, gint len, + guint32 userts) +{ + mblk_t *m; + int err; +#ifdef USE_SENDMSG + m=rtp_session_create_packet_with_data(session,(gchar*)buffer,len,NULL); +#else + m = rtp_session_create_packet(session,RTP_FIXED_HEADER_SIZE,(gchar*)buffer,len); +#endif + err=rtp_session_sendm_with_ts(session,m,userts); + return err; +} + + +static gint +rtp_recv (RtpSession * session, guint32 user_ts) +{ + gint error; + struct sockaddr remaddr; + socklen_t addrlen = sizeof (remaddr); + char *p; + mblk_t *mp; + RtpStream *stream=&session->rtp; + + if (session->rtp.socket<1) return -1; /*session has no sockets for the moment*/ + + + while (1) + { + if (session->rtp.cached_mp==NULL) + session->rtp.cached_mp = allocb (session->max_buf_size, 0); + mp=session->rtp.cached_mp; + if (session->flags & RTP_SESSION_USING_EXT_SOCKETS){ + error=recv(session->rtp.socket,mp->b_wptr,session->max_buf_size,0); + }else error = recvfrom (session->rtp.socket, mp->b_wptr, + session->max_buf_size, 0, + (struct sockaddr *) &remaddr, + &addrlen); + if (error > 0) + { + if (error<RTP_FIXED_HEADER_SIZE){ + g_warning("Packet too small to be a rtp packet (%i)!",error); + stream->stats.bad++; + ortp_global_stats.bad++; + /* don't free, it will be reused next time */ + }else{ + /* resize the memory allocated to fit the udp message */ + + p = g_realloc (mp->b_wptr, error); + if (p != mp->b_wptr) + ortp_debug("The recv area has moved during reallocation."); + mp->b_datap->db_base = mp->b_rptr = + mp->b_wptr = p; + mp->b_wptr += error; + mp->b_datap->db_lim = mp->b_wptr; + /* then parse the message and put on queue */ + rtp_parse (session, mp, user_ts + session->rtp.hwrcv_diff_ts); + session->rtp.cached_mp=NULL; + } + } + else + { + if (error == 0) + { + g_warning + ("rtp_recv: strange... recv() returned zero."); + } + else if (errno!=EWOULDBLOCK && errno!=EAGAIN) + { + if (session->on_network_error.count>0){ + rtp_signal_table_emit3(&session->on_network_error,(gpointer)"Error receiving RTP packet",INT_TO_POINTER(getSocketErrorCode())); + }else g_warning("Error receiving RTP packet: %s.",getSocketError()); + } + /* don't free the cached_mp, it will be reused next time */ + return -1; /* avoids an infinite loop ! */ + } + } + return error; +} + +extern void rtcp_parse(RtpSession *session, mblk_t *mp); + +static gint +rtcp_recv (RtpSession * session) +{ + gint error; + struct sockaddr remaddr; + socklen_t addrlen=0; + char *p; + mblk_t *mp; + + + if (session->rtcp.socket<1) return -1; /*session has no rtcp sockets for the moment*/ + + + while (1) + { + if (session->rtcp.cached_mp==NULL) + session->rtcp.cached_mp = allocb (RTCP_MAX_RECV_BUFSIZE, 0); + + mp=session->rtcp.cached_mp; + if (session->flags & RTP_SESSION_USING_EXT_SOCKETS){ + error=recv(session->rtcp.socket,mp->b_wptr,RTCP_MAX_RECV_BUFSIZE,0); + }else { + addrlen=sizeof (remaddr); + error=recvfrom (session->rtcp.socket, mp->b_wptr, + RTCP_MAX_RECV_BUFSIZE, 0, + (struct sockaddr *) &remaddr, + &addrlen); + } + if (error > 0) + { + /* resize the memory allocated to fit the udp message */ + + p = g_realloc (mp->b_wptr, error); + if (p != mp->b_wptr) + ortp_debug("The recv area has moved during reallocation."); + mp->b_datap->db_base = mp->b_rptr = + mp->b_wptr = p; + mp->b_wptr += error; + mp->b_datap->db_lim = mp->b_wptr; + /* then parse the message */ + rtcp_parse (session, mp); + freemsg(mp); + session->rtcp.cached_mp=NULL; + if (addrlen>0){ + /* store the sender rtcp address to send him receiver reports */ + memcpy(&session->rtcp.rem_addr,&remaddr,addrlen); + } + } + else + { + if (error == 0) + { + g_warning + ("rtcp_recv: strange... recv() returned zero."); + } + else if (errno!=EWOULDBLOCK && errno!=EAGAIN) + { + if (session->on_network_error.count>0){ + rtp_signal_table_emit3(&session->on_network_error,(gpointer)"Error receiving RTCP packet",INT_TO_POINTER(getSocketErrorCode())); + }else g_warning("Error receiving RTCP packet: %s.",getSocketError()); + } + /* don't free the cached_mp, it will be reused next time */ + return -1; /* avoids an infinite loop ! */ + } + } + return error; +} + + +static void payload_type_changed_incoming(RtpSession *session, int paytype){ + /* check if we support this payload type */ + PayloadType *pt=rtp_profile_get_payload(session->profile,paytype); + if (pt!=0){ + g_message ("rtp_parse: payload type changed to %i(%s) !", + paytype,pt->mime_type); + session->payload_type = paytype; + payload_type_changed(session,pt); + rtp_signal_table_emit (&session->on_payload_type_changed); + }else{ + g_warning("Receiving packet with unknown payload type %i.",paytype); + } +} + + +/** + *rtp_session_recvm_with_ts: + *@session: a rtp session. + *@user_ts: a timestamp. + * + * Try to get a rtp packet presented as a mblk_t structure from the rtp session. + * The @user_ts parameter is relative to the first timestamp of the incoming stream. In other + * words, the application does not have to know the first timestamp of the stream, it can + * simply call for the first time this function with @user_ts=0, and then incrementing it + * as it want. The RtpSession takes care of synchronisation between the stream timestamp + * and the user timestamp given here. + * + *Returns: a rtp packet presented as a mblk_t. +**/ + +mblk_t * +rtp_session_recvm_with_ts (RtpSession * session, guint32 user_ts) +{ + mblk_t *mp = NULL; + rtp_header_t *rtp; + guint32 ts; + guint32 packet_time; + RtpScheduler *sched=session->sched; + RtpStream *stream=&session->rtp; + gint rejected=0; + + /* if we are scheduled, remember the scheduler time at which the application has + * asked for its first timestamp */ + + if (session->flags & RTP_SESSION_RECV_NOT_STARTED) + { + + session->rtp.rcv_query_ts_offset = user_ts; + if (session->flags & RTP_SESSION_SCHEDULED) + { + session->rtp.rcv_time_offset = sched->time_; + //g_message("setting snd_time_offset=%i",session->rtp.snd_time_offset); + } + rtp_session_unset_flag (session,RTP_SESSION_RECV_NOT_STARTED); + } + session->rtp.rcv_last_app_ts = user_ts; + rtp_recv (session, user_ts); + rtcp_recv(session); + /* check for telephone event first */ + /* first lock the session */ + rtp_session_lock (session); + mp=getq(&session->rtp.tev_rq); + if (mp!=NULL){ + rtp_signal_table_emit2(&session->on_telephone_event_packet,(gpointer)mp); + if (session->on_telephone_event.count>0){ + rtp_session_check_telephone_events(session,mp); + } + freemsg(mp); + mp=NULL; + } + + /* then now try to return a media packet, if possible */ + /* first condition: if the session is starting, don't return anything + * until the queue size reaches jitt_comp */ + + if (session->flags & RTP_SESSION_RECV_SYNC) + { + rtp_header_t *oldest, *newest; + queue_t *q = &session->rtp.rq; + if (qempty(q)) + { + ortp_debug ("Queue is empty."); + goto end; + } + oldest = (rtp_header_t *) qfirst(q)->b_rptr; + newest = (rtp_header_t *) qlast(q)->b_rptr; + if ((guint32) (newest->timestamp - oldest->timestamp) < + session->rtp.jittctl.jitt_comp_ts) + { + ortp_debug("Not enough packet bufferised."); + goto end; + } + /* enough packet bufferised */ + mp = getq (&session->rtp.rq); + rtp = (rtp_header_t *) mp->b_rptr; + session->rtp.rcv_ts_offset = rtp->timestamp; + /* remember the timestamp offset between the stream timestamp (random) + * and the user timestamp, that very often starts at zero */ + session->rtp.rcv_diff_ts = rtp->timestamp - user_ts; + /* remember the difference between the last received on the socket timestamp and the user timestamp */ + session->rtp.hwrcv_diff_ts=session->rtp.rcv_diff_ts + session->rtp.jittctl.jitt_comp_ts; + session->rtp.rcv_last_ret_ts = user_ts; /* just to have an init value */ + session->rtp.rcv_last_ts = rtp->timestamp; + session->recv_ssrc = rtp->ssrc; + /* delete the recv synchronisation flag */ + rtp_session_unset_flag (session, RTP_SESSION_RECV_SYNC); + ortp_debug("Returning FIRST packet with ts=%i, hwrcv_diff_ts=%i, rcv_diff_ts=%i", rtp->timestamp, + session->rtp.hwrcv_diff_ts,session->rtp.rcv_diff_ts); + + goto end; + } + /* else this the normal case */ + /*calculate the stream timestamp from the user timestamp */ + ts = user_ts + session->rtp.rcv_diff_ts; + session->rtp.rcv_last_ts = ts; + mp = rtp_getq (&session->rtp.rq, ts,&rejected); + + stream->stats.skipped+=rejected; + ortp_global_stats.skipped+=rejected; + + /* perhaps we can now make some checks to see if a resynchronization is needed */ + /* TODO */ + goto end; + + end: + if (mp != NULL) + { + int msgsize = msgdsize (mp); /* evaluate how much bytes (including header) is received by app */ + guint32 packet_ts; + ortp_global_stats.recv += msgsize; + stream->stats.recv += msgsize; + rtp = (rtp_header_t *) mp->b_rptr; + packet_ts=rtp->timestamp; + ortp_debug("Returning mp with ts=%i", packet_ts); + /* check for payload type changes */ + if (session->payload_type != rtp->paytype) + { + payload_type_changed_incoming(session, rtp->paytype); + } + /* patch the packet so that it has a timestamp compensated by the + adaptive jitter buffer mechanism */ + if (session->rtp.jittctl.adaptive){ + rtp->timestamp-=session->rtp.jittctl.corrective_slide; + /*printf("Returned packet has timestamp %u, with clock slide compensated it is %u\n",packet_ts,rtp->timestamp);*/ + } + } + else + { + ortp_debug ("No mp for timestamp queried"); + stream->stats.unavaillable++; + ortp_global_stats.unavaillable++; + } + rtp_session_rtcp_process(session); + rtp_session_unlock (session); + + if (session->flags & RTP_SESSION_SCHEDULED) + { + /* if we are in blocking mode, then suspend the calling process until timestamp + * wanted expires */ + /* but we must not block the process if the timestamp wanted by the application is older + * than current time */ + packet_time = + rtp_session_ts_to_time (session, + user_ts - + session->rtp.rcv_query_ts_offset) + + session->rtp.rcv_time_offset; + ortp_debug ("rtp_session_recvm_with_ts: packet_time=%i, time=%i",packet_time, sched->time_); + wait_point_lock(&session->recv_wp); + if (TIME_IS_STRICTLY_NEWER_THAN (packet_time, sched->time_)) + { + wait_point_wakeup_at(&session->recv_wp,packet_time, (session->flags & RTP_SESSION_BLOCKING_MODE)!=0); + session_set_clr(&sched->r_sessions,session); + } + else session_set_set(&sched->r_sessions,session); /*to unblock _select() immediately */ + wait_point_unlock(&session->recv_wp); + } + return mp; +} + + +gint msg_to_buf (mblk_t * mp, char *buffer, gint len) +{ + gint rlen = len; + mblk_t *m, *mprev; + gint mlen; + m = mp->b_cont; + mprev = mp; + while (m != NULL) + { + mlen = m->b_wptr - m->b_rptr; + if (mlen <= rlen) + { + mblk_t *consumed = m; + memcpy (buffer, m->b_rptr, mlen); + /* go to next mblk_t */ + mprev->b_cont = m->b_cont; + m = m->b_cont; + consumed->b_cont = NULL; + freeb (consumed); + buffer += mlen; + rlen -= mlen; + } + else + { /*if mlen>rlen */ + memcpy (buffer, m->b_rptr, rlen); + m->b_rptr += rlen; + return len; + } + } + return len - rlen; +} + +/** + *rtp_session_recv_with_ts: + *@session: a rtp session. + *@buffer: a user supplied buffer to write the data. + *@len: the length in bytes of the user supplied buffer. + *@time: the timestamp wanted. + *@have_more: the address of an integer to indicate if more data is availlable for the given timestamp. + * + * Tries to read the bytes of the incoming rtp stream related to timestamp @time. In case + * where the user supplied buffer @buffer is not large enough to get all the data + * related to timestamp @time, then *( @have_more) is set to 1 to indicate that the application + * should recall the function with the same timestamp to get more data. + * + * When the rtp session is scheduled (see rtp_session_set_scheduling_mode() ), and the + * blocking mode is on (see rtp_session_set_blocking_mode() ), then the calling thread + * is suspended until the timestamp given as argument expires, whatever a received packet + * fits the query or not. + * + * Important note: it is clear that the application cannot know the timestamp of the first + * packet of the incoming stream, because it can be random. The @time timestamp given to the + * function is used relatively to first timestamp of the stream. In simple words, 0 is a good + * value to start calling this function. + * + * This function internally calls rtp_session_recvm_with_ts() to get a rtp packet. The content + * of this packet is then copied into the user supplied buffer in an intelligent manner: + * the function takes care of the size of the supplied buffer and the timestamp given in + * argument. Using this function it is possible to read continous audio data (e.g. pcma,pcmu...) + * with for example a standart buffer of size of 160 with timestamp incrementing by 160 while the incoming + * stream has a different packet size. + * + *Returns: if a packet was availlable with the corresponding timestamp supplied in argument + * then the number of bytes written in the user supplied buffer is returned. If no packets + * are availlable, either because the sender has not started to send the stream, or either + * because silence packet are not transmitted, or either because the packet was lost during + * network transport, then the function returns zero. +**/ +gint rtp_session_recv_with_ts (RtpSession * session, gchar * buffer, + gint len, guint32 time, gint * have_more) +{ + mblk_t *mp; + gint rlen = len; + gint wlen, mlen; + guint32 ts_int = 0; /*the length of the data returned in the user supplied buffer, in TIMESTAMP UNIT */ + PayloadType *payload; + RtpStream *stream=&session->rtp; + + *have_more = 0; + + mp = rtp_session_recvm_with_ts (session, time); + payload =rtp_profile_get_payload (session->profile, + session->payload_type); + if (payload==NULL){ + g_warning("rtp_session_recv_with_ts: unable to recv an unsupported payload."); + if (mp!=NULL) freemsg(mp); + return -1; + } + if (!(session->flags & RTP_SESSION_RECV_SYNC)) + { + //ortp_debug("time=%i rcv_last_ret_ts=%i",time,session->rtp.rcv_last_ret_ts); + if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN + (time, session->rtp.rcv_last_ret_ts)) + { + /* the user has missed some data previously, so we are going to give him now. */ + /* we must tell him to call the function once again with the same timestamp + * by setting *have_more=1 */ + *have_more = 1; + } + if (payload->type == PAYLOAD_AUDIO_CONTINUOUS) + { + ts_int = (len * payload->bits_per_sample) >> 3; + session->rtp.rcv_last_ret_ts += ts_int; + //ortp_debug("ts_int=%i",ts_int); + } + else + ts_int = 0; + } + else return 0; + + /* try to fill the user buffer */ + while (1) + { + + if (mp != NULL) + { + mlen = msgdsize (mp->b_cont); + wlen = msg_to_buf (mp, buffer, rlen); + buffer += wlen; + rlen -= wlen; + ortp_debug("mlen=%i wlen=%i rlen=%i", mlen, wlen, + rlen); + /* do we fill all the buffer ? */ + if (rlen > 0) + { + /* we did not fill all the buffer */ + freemsg (mp); + /* if we have continuous audio, try to get other packets to fill the buffer, + * ie continue the loop */ + //ortp_debug("User buffer not filled entirely"); + if (ts_int > 0) + { + time = session->rtp.rcv_last_ret_ts; + ortp_debug("Need more: will ask for %i.", + time); + } + else + return len - rlen; + } + else if (mlen > wlen) + { + int unread = + mlen - wlen + (mp->b_wptr - + mp->b_rptr); + /* not enough space in the user supplied buffer */ + /* we re-enqueue the msg with its updated read pointers for next time */ + ortp_debug ("Re-enqueuing packet."); + rtp_session_lock (session); + rtp_putq (&session->rtp.rq, mp); + rtp_session_unlock (session); + /* quite ugly: I change the stats ... */ + ortp_global_stats.recv -= unread; + stream->stats.recv -= unread; + return len; + } + else + { + /* the entire packet was written to the user buffer */ + freemsg (mp); + return len; + } + } + else + { + /* fill with a zero pattern (silence) */ + if (payload->pattern_length != 0) + { + int i = 0, j = 0; + while (i < rlen) + { + buffer[i] = payload->zero_pattern[j]; + i++; + j++; + if (j <= payload->pattern_length) + j = 0; + } + return len; + } + *have_more = 0; + return 0; + } + mp = rtp_session_recvm_with_ts (session, time); + payload = rtp_profile_get_payload (session->profile, + session->payload_type); + if (payload==NULL){ + g_warning("rtp_session_recv_with_ts: unable to recv an unsupported payload."); + if (mp!=NULL) freemsg(mp); + return -1; + } + } + return -1; +} +/** + *rtp_session_get_current_send_ts: + *@session: a rtp session. + * + * When the rtp session is scheduled and has started to send packets, this function + * computes the timestamp that matches to the present time. Using this function can be + * usefull when sending discontinuous streams. Some time can be elapsed between the end + * of a stream burst and the begin of a new stream burst, and the application may be not + * not aware of this elapsed time. In order to get a valid (current) timestamp to pass to + * #rtp_session_send_with_ts() or #rtp_session_sendm_with_ts(), the application may + * use rtp_session_get_current_send_ts(). + * + *Returns: the current send timestamp for the rtp session. +**/ +guint32 rtp_session_get_current_send_ts(RtpSession *session) +{ + guint32 userts; + guint32 session_time; + RtpScheduler *sched=session->sched; + PayloadType *payload; + g_return_val_if_fail (session->payload_type<128, 0); + payload=rtp_profile_get_payload(session->profile,session->payload_type); + g_return_val_if_fail(payload!=NULL, 0); + if ( (session->flags & RTP_SESSION_SCHEDULED)==0 ){ + g_warning("can't guess current timestamp because session is not scheduled."); + return 0; + } + session_time=sched->time_-session->rtp.snd_time_offset; + userts= (guint32)( ( (gdouble)(session_time) * (gdouble) payload->clock_rate )/ 1000.0) + + session->rtp.snd_ts_offset; + return userts; +} + +/** + *rtp_session_get_current_recv_ts: + *@session: a rtp session. + * + * Same thing as rtp_session_get_current_send_ts() except that it's for an incoming stream. + * Works only on scheduled mode. + * + * Returns: the theoritical that would have to be receive now. + * +**/ +guint32 rtp_session_get_current_recv_ts(RtpSession *session){ + guint32 userts; + guint32 session_time; + RtpScheduler *sched=ortp_get_scheduler(); + PayloadType *payload; + g_return_val_if_fail (session->payload_type<128, 0); + payload=rtp_profile_get_payload(session->profile,session->payload_type); + g_return_val_if_fail(payload!=NULL, 0); + if ( (session->flags & RTP_SESSION_SCHEDULED)==0 ){ + g_warning("can't guess current timestamp because session is not scheduled."); + return 0; + } + session_time=sched->time_-session->rtp.rcv_time_offset; + userts= (guint32)( ( (gdouble)(session_time) * (gdouble) payload->clock_rate )/ 1000.0) + + session->rtp.rcv_ts_offset; + return userts; +} + +/** + *rtp_session_set_time_jump_limit: + *@session: the rtp session + *@ts_step: a time interval in miliseconds + * + * oRTP has the possibility to inform the application through a callback registered + * with rtp_session_signal_connect about crazy incoming RTP stream that jumps from + * a timestamp N to N+<some crazy value>. This lets the opportunity for the application + * to reset the session in order to resynchronize, or any other action like stopping the call + * and reporting an error. +**/ +void rtp_session_set_time_jump_limit(RtpSession *session, gint milisecs){ + guint32 ts; + session->rtp.time_jump=milisecs; + ts=rtp_session_time_to_ts(session,milisecs); + if (ts==0) session->rtp.ts_jump=1<<31; /* do not detect ts jump */ + else session->rtp.ts_jump=ts; +} + +void rtp_session_uninit (RtpSession * session) +{ + /* first of all remove the session from the scheduler */ + if (session->flags & RTP_SESSION_SCHEDULED) + { + rtp_scheduler_remove_session (session->sched,session); + } + /*flush all queues */ + flushq (&session->rtp.rq, FLUSHALL); + + /* close sockets */ + close_socket (session->rtp.socket); + close_socket (session->rtcp.socket); + + wait_point_uninit(&session->send_wp); + wait_point_uninit(&session->recv_wp); + g_mutex_free (session->lock); + session->lock=NULL; + if (session->current_tev!=NULL) freemsg(session->current_tev); + if (session->rtp.cached_mp!=NULL) freemsg(session->rtp.cached_mp); + if (session->rtcp.cached_mp!=NULL) freemsg(session->rtcp.cached_mp); + if (session->sd!=NULL) freemsg(session->sd); +} + +/** + *rtp_session_reset: + *@session: a rtp session. + * + * Reset the session: local and remote addresses are kept unchanged but the internal + * queue for ordering and buffering packets is flushed, the session is ready to be + * re-synchronised to another incoming stream. + * +**/ +void rtp_session_reset (RtpSession * session) +{ + + if (session->flags & RTP_SESSION_SCHEDULED) rtp_session_lock (session); + + flushq (&session->rtp.rq, FLUSHALL); + rtp_session_set_flag (session, RTP_SESSION_RECV_SYNC); + rtp_session_set_flag (session, RTP_SESSION_SEND_SYNC); + rtp_session_set_flag (session, RTP_SESSION_RECV_NOT_STARTED); + rtp_session_set_flag (session, RTP_SESSION_SEND_NOT_STARTED); + //session->ssrc=0; + session->rtp.snd_time_offset = 0; + session->rtp.snd_ts_offset = 0; + session->rtp.snd_rand_offset = 0; + session->rtp.snd_last_ts = 0; + session->rtp.rcv_time_offset = 0; + session->rtp.rcv_ts_offset = 0; + session->rtp.rcv_query_ts_offset = 0; + session->rtp.rcv_diff_ts = 0; + session->rtp.rcv_ts = 0; + session->rtp.rcv_last_ts = 0; + session->rtp.rcv_last_app_ts = 0; + session->rtp.hwrcv_extseq.one = 0; + session->rtp.hwrcv_since_last_SR=0; + session->rtp.snd_seq = 0; + rtp_stats_reset(&session->rtp.stats); + jitter_control_init(&session->rtp.jittctl,-1,NULL); + + if (session->flags & RTP_SESSION_SCHEDULED) rtp_session_unlock (session); + +} + +/** + *rtp_session_destroy: + *@session: a rtp session. + * + * Destroys a rtp session. + * +**/ +void rtp_session_destroy (RtpSession * session) +{ + rtp_session_uninit (session); + g_free (session); +} + +guint32 rtp_session_time_to_ts(RtpSession *session, gint time){ + PayloadType *payload; + g_return_val_if_fail (session->payload_type < 127, 0); + payload = + rtp_profile_get_payload (session->profile, + session->payload_type); + if (payload == NULL) + { + g_warning + ("rtp_session_ts_to_t: use of unsupported payload type."); + return 0; + } + /* the return value is in milisecond */ + return (double)payload->clock_rate*(double)time/1000.0; +} + +/* function used by the scheduler only:*/ +guint32 rtp_session_ts_to_time (RtpSession * session, guint32 timestamp) +{ + PayloadType *payload; + g_return_val_if_fail (session->payload_type < 127, 0); + payload = + rtp_profile_get_payload (session->profile, + session->payload_type); + if (payload == NULL) + { + g_warning + ("rtp_session_ts_to_t: use of unsupported payload type."); + return 0; + } + /* the return value is in milisecond */ + return (guint32) (1000.0 * + ((double) timestamp / + (double) payload->clock_rate)); +} + + +/* time is the number of miliseconds elapsed since the start of the scheduler */ +void rtp_session_process (RtpSession * session, guint32 time, RtpScheduler *sched) +{ + wait_point_lock(&session->send_wp); + if (wait_point_check(&session->send_wp,time)){ + session_set_set(&sched->w_sessions,session); + wait_point_wakeup(&session->send_wp); + } + wait_point_unlock(&session->send_wp); + + wait_point_lock(&session->recv_wp); + if (wait_point_check(&session->recv_wp,time)){ + session_set_set(&sched->r_sessions,session); + wait_point_wakeup(&session->recv_wp); + } + wait_point_unlock(&session->recv_wp); +} + + +void rtp_session_make_time_distorsion(RtpSession *session, gint milisec) +{ + session->rtp.snd_time_offset+=milisec; +} + + +/* packet api */ + +void rtp_add_csrc(mblk_t *mp, guint32 csrc) +{ + rtp_header_t *hdr=(rtp_header_t*)mp->b_rptr; + hdr->csrc[hdr->cc]=csrc; + hdr->cc++; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.h new file mode 100644 index 00000000..e7702000 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsession.h @@ -0,0 +1,287 @@ + /* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef RTPSESSION_H +#define RTPSESSION_H + + +#include <rtpport.h> +#include <rtp.h> +#include <payloadtype.h> +#include <sessionset.h> +#include <rtcp.h> +#include <str_utils.h> +#include <rtpsignaltable.h> + +#include <stdio.h> + + +#ifndef _WIN32 +# include <sys/types.h> +# include <sys/socket.h> +# include <errno.h> +# include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +# include <unistd.h> +# include <sys/time.h> +#else +# include <winsock2.h> +#endif /* _WIN32 */ + + + +typedef enum { + RTP_SESSION_RECVONLY, + RTP_SESSION_SENDONLY, + RTP_SESSION_SENDRECV +} RtpSessionMode; + + + +typedef enum { + RTP_SESSION_RECV_SYNC=1, /* the rtp session is synchronising in the incoming stream */ + RTP_SESSION_SEND_SYNC=1<<1, /* the rtp session is synchronising in the outgoing stream */ + RTP_SESSION_SCHEDULED=1<<2, /* the rtp session has to be scheduled */ + RTP_SESSION_BLOCKING_MODE=1<<3, /* in blocking mode */ + RTP_SESSION_RECV_NOT_STARTED=1<<4, /* the application has not started to try to recv */ + RTP_SESSION_SEND_NOT_STARTED=1<<5, /* the application has not started to send something */ + RTP_SESSION_IN_SCHEDULER=1<<6, /* the rtp session is in the scheduler list */ + RTP_SESSION_USING_EXT_SOCKETS=1<<7 /* the session is using externaly supplied sockets */ +}RtpSessionFlags; + + +typedef struct _JitterControl +{ + gint jitt_comp; /* the user jitt_comp in miliseconds*/ + gint jitt_comp_ts; /* the jitt_comp converted in rtp time (same unit as timestamp) */ + gint adapt_jitt_comp_ts; + float slide; + float jitter; + gint count; + gint olddiff; + float inter_jitter; /* interarrival jitter as defined in the RFC */ + gint corrective_step; + gint corrective_slide; + gboolean adaptive; +} JitterControl; + +typedef struct _WaitPoint +{ + GMutex *lock; + GCond *cond; + guint32 time; + gboolean wakeup; +} WaitPoint; + +typedef struct _RtpStream +{ + gint socket; + gint socktype; + gint max_rq_size; + gint time_jump; + guint32 ts_jump; + queue_t rq; + queue_t tev_rq; + mblk_t *cached_mp; +#ifdef INET6 + struct sockaddr_storage loc_addr; + struct sockaddr_storage rem_addr; +#else + struct sockaddr_in loc_addr; + struct sockaddr_in rem_addr; +#endif + int addrlen; + JitterControl jittctl; + guint32 snd_time_offset;/*the scheduler time when the application send its first timestamp*/ + guint32 snd_ts_offset; /* the first application timestamp sent by the application */ + guint32 snd_rand_offset; /* a random number added to the user offset to make the stream timestamp*/ + guint32 snd_last_ts; /* the last stream timestamp sended */ + guint32 rcv_time_offset; /*the scheduler time when the application ask for its first timestamp*/ + guint32 rcv_ts_offset; /* the first stream timestamp */ + guint32 rcv_query_ts_offset; /* the first user timestamp asked by the application */ + guint32 rcv_diff_ts; /* difference between the first user timestamp and first stream timestamp */ + guint32 hwrcv_diff_ts; + guint32 rcv_ts; /* to be unused */ + guint32 rcv_last_ts; /* the last stream timestamp got by the application */ + guint32 rcv_last_app_ts; /* the last application timestamp asked by the application */ + guint32 rcv_last_ret_ts; /* the timestamp of the last sample returned (only for continuous audio)*/ + poly32_t hwrcv_extseq; /* last received on socket extended sequence number */ + guint32 hwrcv_seq_at_last_SR; + guint hwrcv_since_last_SR; + guint32 last_rcv_SR_ts; /* NTP timestamp (middle 32 bits) of last received SR */ + struct timeval last_rcv_SR_time; /* time at which last SR was received */ + guint16 snd_seq; /* send sequence number */ + guint32 last_rtcp_report_snt_r; /* the time of the last rtcp report sent, in recv timestamp unit */ + guint32 last_rtcp_report_snt_s; /* the time of the last rtcp report sent, in send timestamp unit */ + guint32 rtcp_report_snt_interval; /* the interval in timestamp unit between rtcp report sent */ + rtp_stats_t stats; +}RtpStream; + +typedef struct _RtcpStream +{ + gint socket; + gint socktype; + mblk_t *cached_mp; +#ifdef INET6 + struct sockaddr_storage loc_addr; + struct sockaddr_storage rem_addr; +#else + struct sockaddr_in loc_addr; + struct sockaddr_in rem_addr; +#endif + int addrlen; +} RtcpStream; + +typedef struct _RtpSession RtpSession; + + + +struct _RtpSession +{ + RtpSession *next; /* next RtpSession, when the session are enqueued by the scheduler */ + RtpProfile *profile; + WaitPoint recv_wp; + WaitPoint send_wp; + GMutex *lock; + guint32 send_ssrc; + guint32 recv_ssrc; + gint payload_type; + gint max_buf_size; + RtpSignalTable on_ssrc_changed; + RtpSignalTable on_payload_type_changed; + RtpSignalTable on_telephone_event_packet; + RtpSignalTable on_telephone_event; + RtpSignalTable on_timestamp_jump; + RtpSignalTable on_network_error; + struct _OList *signal_tables; + RtpStream rtp; + RtcpStream rtcp; + RtpSessionMode mode; + struct _RtpScheduler *sched; + guint32 flags; + gint mask_pos; /* the position in the scheduler mask of RtpSession */ + gpointer user_data; + + /* telephony events extension */ + gint telephone_events_pt; /* the payload type used for telephony events */ + mblk_t *current_tev; /* the pending telephony events */ + mblk_t *sd; + queue_t contributing_sources; +}; + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*private */ +void rtp_session_init(RtpSession *session, gint mode); +#define rtp_session_lock(session) g_mutex_lock(session->lock) +#define rtp_session_unlock(session) g_mutex_unlock(session->lock) +#define rtp_session_set_flag(session,flag) (session)->flags|=(flag) +#define rtp_session_unset_flag(session,flag) (session)->flags&=~(flag) +void rtp_session_uninit(RtpSession *session); + +/* public API */ +RtpSession *rtp_session_new(gint mode); +void rtp_session_set_scheduling_mode(RtpSession *session, gint yesno); +void rtp_session_set_blocking_mode(RtpSession *session, gint yesno); +void rtp_session_set_profile(RtpSession *session,RtpProfile *profile); +#define rtp_session_get_profile(session) (session)->profile +int rtp_session_signal_connect(RtpSession *session,const gchar *signal, RtpCallback cb, gpointer user_data); +int rtp_session_signal_disconnect_by_callback(RtpSession *session,const gchar *signal, RtpCallback cb); +void rtp_session_set_ssrc(RtpSession *session, guint32 ssrc); +void rtp_session_set_seq_number(RtpSession *session, guint16 seq); +guint16 rtp_session_get_seq_number(RtpSession *session); +void rtp_session_set_jitter_compensation(RtpSession *session, int milisec); +void rtp_session_enable_adaptive_jitter_compensation(RtpSession *session, gboolean val); +gboolean rtp_session_adaptive_jitter_compensation_enabled(RtpSession *session); +void rtp_session_set_time_jump_limit(RtpSession *session, gint miliseconds); +int rtp_session_set_local_addr(RtpSession *session,const gchar *addr, gint port); +gint rtp_session_set_remote_addr(RtpSession *session,const gchar *addr, gint port); +/* alternatively to the set_remote_addr() and set_local_addr(), an application can give +a valid socket (potentially connect()ed )to be used by the RtpSession */ +void rtp_session_set_sockets(RtpSession *session, gint rtpfd, gint rtcpfd); +int rtp_session_set_payload_type(RtpSession *session, int paytype); +int rtp_session_get_payload_type(RtpSession *session); +int rtp_session_set_payload_type_with_string (RtpSession * session, const char * mime); +/*low level recv and send functions */ +mblk_t * rtp_session_recvm_with_ts (RtpSession * session, guint32 user_ts); +mblk_t * rtp_session_create_packet(RtpSession *session,gint header_size, const char *payload, gint payload_size); +mblk_t * rtp_session_create_packet_with_data(RtpSession *session, char *payload, gint payload_size, void (*freefn)(void*)); +mblk_t * rtp_session_create_packet_in_place(RtpSession *session,char *buffer, gint size, void (*freefn)(void*) ); +gint rtp_session_sendm_with_ts (RtpSession * session, mblk_t *mp, guint32 userts); +/* high level recv and send functions */ +gint rtp_session_recv_with_ts(RtpSession *session, gchar *buffer, gint len, guint32 time, gint *have_more); +gint rtp_session_send_with_ts(RtpSession *session, const gchar *buffer, gint len, guint32 userts); + + +guint32 rtp_session_get_current_send_ts(RtpSession *session); +guint32 rtp_session_get_current_recv_ts(RtpSession *session); +void rtp_session_flush_sockets(RtpSession *session); +void rtp_session_reset(RtpSession *session); +void rtp_session_destroy(RtpSession *session); + +#define rtp_session_get_stats(session) (&(session)->stats) +#define rtp_session_reset_stats(session) memset(&(session)->stats,0,sizeof(rtp_stats_t)) +#define rtp_session_set_data(session,data) (session)->user_data=(data) +#define rtp_session_get_data(session,data) ((session)->user_data) + +#define rtp_session_max_buf_size_set(session,bufsize) (session)->max_buf_size=(bufsize) + +/* in use with the scheduler to convert a timestamp in scheduler time unit (ms) */ +guint32 rtp_session_ts_to_time(RtpSession *session,guint32 timestamp); +guint32 rtp_session_time_to_ts(RtpSession *session, gint time); +/* this function aims at simulating senders with "imprecise" clocks, resulting in +rtp packets sent with timestamp uncorrelated with the system clock . +This is only availlable to sessions working with the oRTP scheduler */ +void rtp_session_make_time_distorsion(RtpSession *session, gint milisec); + +/*RTCP functions */ +void rtp_session_set_source_description(RtpSession *session, const gchar *cname, + const gchar *name, const gchar *email, const gchar *phone, + const gchar *loc, const gchar *tool, const gchar *note); +void rtp_session_add_contributing_source(RtpSession *session, guint32 csrc, + const gchar *cname, const gchar *name, const gchar *email, const gchar *phone, + const gchar *loc, const gchar *tool, const gchar *note); +void rtp_session_remove_contributing_sources(RtpSession *session, guint32 csrc); +mblk_t* rtp_session_create_rtcp_sdes_packet(RtpSession *session); + + +/* packet api */ +/* the first argument is a mblk_t. The header is supposed to be not splitted */ +#define rtp_set_markbit(mp,value) ((rtp_header_t*)((mp)->b_rptr))->markbit=(value) +#define rtp_set_seqnumber(mp,seq) ((rtp_header_t*)((mp)->b_rptr))->seq_number=(seq) +#define rtp_set_timestamp(mp,ts) ((rtp_header_t*)((mp)->b_rptr))->timestamp=(ts) +#define rtp_set_ssrc(mp,_ssrc) ((rtp_header_t*)((mp)->b_rptr))->ssrc=(_ssrc) +void rtp_add_csrc(mblk_t *mp,guint32 csrc); +#define rtp_set_payload_type(mp,pt) ((rtp_header_t*)((mp)->b_rptr))->paytype=(pt) + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.c new file mode 100644 index 00000000..35b19ae3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.c @@ -0,0 +1,98 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#include <rtpsession.h> +#include "utils.h" + + +void rtp_signal_table_init(RtpSignalTable *table,RtpSession *session, char *signal_name) +{ + memset(table,0,sizeof(RtpSignalTable)); + table->session=session; + table->signal_name=signal_name; + session->signal_tables=o_list_append(session->signal_tables,(gpointer)table); +} + +int rtp_signal_table_add(RtpSignalTable *table,RtpCallback cb, gpointer user_data) +{ + gint i; + + for (i=0;i<RTP_CALLBACK_TABLE_MAX_ENTRIES;i++){ + if (table->callback[i]==NULL){ + table->callback[i]=cb; + table->user_data[i]=user_data; + table->count++; + return 0; + } + } + return -1; +} + +void rtp_signal_table_emit(RtpSignalTable *table) +{ + gint i,c; + + for (i=0,c=0;c<table->count;i++){ + if (table->callback[i]!=NULL){ + c++; /*I like it*/ + table->callback[i](table->session,table->user_data[i]); + } + } +} + +void rtp_signal_table_emit2(RtpSignalTable *table, gpointer arg) +{ + gint i,c; + + for (i=0,c=0;c<table->count;i++){ + if (table->callback[i]!=NULL){ + c++; /*I like it*/ + table->callback[i](table->session,arg,table->user_data[i]); + } + } +} + +void rtp_signal_table_emit3(RtpSignalTable *table, gpointer arg1, gpointer arg2) +{ + gint i,c; + + for (i=0,c=0;c<table->count;i++){ + if (table->callback[i]!=NULL){ + c++; /*I like it*/ + table->callback[i](table->session,arg1,arg2,table->user_data[i]); + } + } +} + +int rtp_signal_table_remove_by_callback(RtpSignalTable *table,RtpCallback cb) +{ + gint i; + + for (i=0;i<RTP_CALLBACK_TABLE_MAX_ENTRIES;i++){ + if (table->callback[i]==cb){ + table->callback[i]=NULL; + table->user_data[i]=NULL; + table->count--; + return 0; + } + } + return -1; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.h new file mode 100644 index 00000000..c6cbe960 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtpsignaltable.h @@ -0,0 +1,47 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define RTP_CALLBACK_TABLE_MAX_ENTRIES 5 + +typedef void (*RtpCallback)(struct _RtpSession *, ...); + +struct _RtpSignalTable +{ + RtpCallback callback[RTP_CALLBACK_TABLE_MAX_ENTRIES]; + gpointer user_data[RTP_CALLBACK_TABLE_MAX_ENTRIES]; + struct _RtpSession *session; + const char *signal_name; + gint count; +}; + +typedef struct _RtpSignalTable RtpSignalTable; + +void rtp_signal_table_init(RtpSignalTable *table,struct _RtpSession *session, char *signal_name); + +int rtp_signal_table_add(RtpSignalTable *table,RtpCallback cb, gpointer user_data); + +void rtp_signal_table_emit(RtpSignalTable *table); + +/* emit but with a second arg */ +void rtp_signal_table_emit2(RtpSignalTable *table, gpointer arg); + +/* emit but with a third arg */ +void rtp_signal_table_emit3(RtpSignalTable *table, gpointer arg1, gpointer arg2); + +int rtp_signal_table_remove_by_callback(RtpSignalTable *table,RtpCallback cb); diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.c new file mode 100644 index 00000000..6ac57e72 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.c @@ -0,0 +1,32 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "rtptimer.h" + +void rtp_timer_set_interval(RtpTimer *timer, struct timeval *interval) +{ + if (timer->state==RTP_TIMER_RUNNING){ + g_warning("Cannot change timer interval while it is running.\n"); + return; + } + timer->interval.tv_sec=interval->tv_sec; + timer->interval.tv_usec=interval->tv_usec; +} + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.h new file mode 100644 index 00000000..081e0892 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/rtptimer.h @@ -0,0 +1,52 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef RTPTIMER_H +#define RTPTIMER_H + +#ifndef _WIN32 +#include <sys/time.h> +#else +#include <time.h> +#include "winsock2.h" +#endif + +#include <rtpport.h> + + +typedef void (*RtpTimerFunc)(void); + +struct _RtpTimer +{ + gint state; +#define RTP_TIMER_RUNNING 1 +#define RTP_TIMER_STOPPED 0 + RtpTimerFunc timer_init; + RtpTimerFunc timer_do; + RtpTimerFunc timer_uninit; + struct timeval interval; +}; + +typedef struct _RtpTimer RtpTimer; + +void rtp_timer_set_interval(RtpTimer *timer, struct timeval *interval); + +extern RtpTimer posix_timer; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.c new file mode 100644 index 00000000..17ff6748 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.c @@ -0,0 +1,235 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _WIN32 /* do not include ortp-config.h when we are on win32 */ + #include <rtpport.h> + #include <sched.h> + #include <unistd.h> + #include <errno.h> +#else + #include "ortp-config-win32.h" +#endif + + + +#include "scheduler.h" + +// To avoid warning during compile +extern void rtp_session_process (RtpSession * session, guint32 time, RtpScheduler *sched); + + +void rtp_scheduler_init(RtpScheduler *sched) +{ + sched->list=0; + sched->time_=0; + /* default to the posix timer */ + rtp_scheduler_set_timer(sched,&posix_timer); + sched->lock=g_mutex_new(); + //sched->unblock_select_mutex=g_mutex_new(); + sched->unblock_select_cond=g_cond_new(); + sched->max_sessions=sizeof(SessionSet)*8; + session_set_init(&sched->all_sessions); + sched->all_max=0; + session_set_init(&sched->r_sessions); + sched->r_max=0; + session_set_init(&sched->w_sessions); + sched->w_max=0; + session_set_init(&sched->e_sessions); + sched->e_max=0; +} + +RtpScheduler * rtp_scheduler_new() +{ + RtpScheduler *sched=g_malloc(sizeof(RtpScheduler)); + memset(sched,0,sizeof(RtpScheduler)); + rtp_scheduler_init(sched); + return sched; +} + +void rtp_scheduler_set_timer(RtpScheduler *sched,RtpTimer *timer) +{ + if (sched->thread_running){ + g_warning("Cannot change timer while the scheduler is running !!"); + return; + } + sched->timer=timer; + /* report the timer increment */ + sched->timer_inc=(timer->interval.tv_usec/1000) + (timer->interval.tv_sec*1000); +} + +void rtp_scheduler_start(RtpScheduler *sched) +{ + if (sched->thread_running==0){ + sched->thread_running=1; + g_mutex_lock(sched->lock); + sched->thread=g_thread_create((GThreadFunc)rtp_scheduler_schedule,(gpointer)sched,TRUE,NULL); + g_cond_wait(sched->unblock_select_cond,sched->lock); + g_mutex_unlock(sched->lock); + } + else g_warning("Scheduler thread already running."); + +} +void rtp_scheduler_stop(RtpScheduler *sched) +{ + if (sched->thread_running==1) + { + sched->thread_running=0; + g_thread_join(sched->thread); + } + else g_warning("Scheduler thread is not running."); +} + +void rtp_scheduler_destroy(RtpScheduler *sched) +{ + if (sched->thread_running) rtp_scheduler_stop(sched); + g_mutex_free(sched->lock); + //g_mutex_free(sched->unblock_select_mutex); + g_cond_free(sched->unblock_select_cond); + g_free(sched); +} + +gpointer rtp_scheduler_schedule(gpointer psched) +{ + RtpScheduler *sched=(RtpScheduler*) psched; + RtpTimer *timer=sched->timer; + RtpSession *current; + int err; + + /* try to get the real time priority by getting root*/ +#ifndef _WIN32 +#ifdef HAVE_SETEUID + err=seteuid(0); +#else + err=setuid(0); +#endif + if (err<0) g_message("Could not get root euid: %s",strerror(errno)); +#endif + g_message("scheduler: trying to reach real time kernel scheduling..."); + + /* take this lock to prevent the thread to start until g_thread_create() returns + because we need sched->thread to be initialized */ + g_mutex_lock(sched->lock); + g_cond_signal(sched->unblock_select_cond); /* unblock the starting thread */ + g_mutex_unlock(sched->lock); + g_thread_set_priority(sched->thread,G_THREAD_PRIORITY_HIGH); + timer->timer_init(); + while(sched->thread_running) + { + /* do the processing here: */ + + g_mutex_lock(sched->lock); + + current=sched->list; + /* processing all scheduled rtp sessions */ + while (current!=NULL) + { + ortp_debug("scheduler: processing session=%p.\n",current); + rtp_session_process(current,sched->time_,sched); + current=current->next; + } + /* wake up all the threads that are sleeping in _select() */ + g_cond_broadcast(sched->unblock_select_cond); + g_mutex_unlock(sched->lock); + + /* now while the scheduler is going to sleep, the other threads can compute their + result mask and see if they have to leave, or to wait for next tick*/ + //g_message("scheduler: sleeping."); + timer->timer_do(); + sched->time_+=sched->timer_inc; + } + /* when leaving the thread, stop the timer */ + timer->timer_uninit(); + return NULL; +} + +void rtp_scheduler_add_session(RtpScheduler *sched, RtpSession *session) +{ + RtpSession *oldfirst; + int i; + if (session->flags & RTP_SESSION_IN_SCHEDULER){ + /* the rtp session is already scheduled, so return silently */ + return; + } + rtp_scheduler_lock(sched); + /* enqueue the session to the list of scheduled sessions */ + oldfirst=sched->list; + sched->list=session; + session->next=oldfirst; + if (sched->max_sessions==0){ + g_error("rtp_scheduler_add_session: max_session=0 !"); + } + /* find a free pos in the session mask*/ + for (i=0;i<sched->max_sessions;i++){ + if (!ORTP_FD_ISSET(i,&sched->all_sessions.rtpset)){ + session->mask_pos=i; + session_set_set(&sched->all_sessions,session); + /* make a new session scheduled not blockable if it has not started*/ + if (session->flags & RTP_SESSION_RECV_NOT_STARTED) + session_set_set(&sched->r_sessions,session); + if (session->flags & RTP_SESSION_SEND_NOT_STARTED) + session_set_set(&sched->w_sessions,session); + if (i>sched->all_max){ + sched->all_max=i; + } + break; + } + } + + rtp_session_set_flag(session,RTP_SESSION_IN_SCHEDULER); + rtp_scheduler_unlock(sched); +} + +void rtp_scheduler_remove_session(RtpScheduler *sched, RtpSession *session) +{ + RtpSession *tmp; + int cond=1; + g_return_if_fail(session!=NULL); + if (!(session->flags & RTP_SESSION_IN_SCHEDULER)){ + /* the rtp session is not scheduled, so return silently */ + return; + } + + rtp_scheduler_lock(sched); + tmp=sched->list; + if (tmp==session){ + sched->list=tmp->next; + rtp_session_unset_flag(session,RTP_SESSION_IN_SCHEDULER); + session_set_clr(&sched->all_sessions,session); + rtp_scheduler_unlock(sched); + return; + } + /* go the position of session in the list */ + while(cond){ + if (tmp!=NULL){ + if (tmp->next==session){ + tmp->next=tmp->next->next; + cond=0; + } + else tmp=tmp->next; + }else { + /* the session was not found ! */ + g_warning("rtp_scheduler_remove_session: the session was not found in the scheduler list!"); + cond=0; + } + } + rtp_session_unset_flag(session,RTP_SESSION_IN_SCHEDULER); + /* delete the bit in the mask */ + session_set_clr(&sched->all_sessions,session); + rtp_scheduler_unlock(sched); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.h new file mode 100644 index 00000000..91cde6a9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/scheduler.h @@ -0,0 +1,70 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCHEDULER_H +#define SCHEDULER_H + +#include <rtpsession.h> +#include <sessionset.h> +#include "rtptimer.h" +#include "port_fct.h" + + +struct _RtpScheduler { + + RtpSession *list; /* list of scheduled sessions*/ + SessionSet all_sessions; /* mask of scheduled sessions */ + gint all_max; /* the highest pos in the all mask */ + SessionSet r_sessions; /* mask of sessions that have a recv event */ + gint r_max; + SessionSet w_sessions; /* mask of sessions that have a send event */ + gint w_max; + SessionSet e_sessions; /* mask of session that have error event */ + gint e_max; + gint max_sessions; /* the number of position in the masks */ + /* GMutex *unblock_select_mutex; */ + GCond *unblock_select_cond; + GMutex *lock; + GThread *thread; + gint thread_running; + struct _RtpTimer *timer; + guint32 time_; /*number of miliseconds elapsed since the start of the thread */ + guint32 timer_inc; /* the timer increment in milisec */ +}; + +typedef struct _RtpScheduler RtpScheduler; + +RtpScheduler * rtp_scheduler_new(); +void rtp_scheduler_set_timer(RtpScheduler *sched,RtpTimer *timer); +void rtp_scheduler_start(RtpScheduler *sched); +void rtp_scheduler_stop(RtpScheduler *sched); +void rtp_scheduler_destroy(RtpScheduler *sched); + +void rtp_scheduler_add_session(RtpScheduler *sched, RtpSession *session); +void rtp_scheduler_remove_session(RtpScheduler *sched, RtpSession *session); + +gpointer rtp_scheduler_schedule(gpointer sched); + +#define rtp_scheduler_lock(sched) g_mutex_lock((sched)->lock) +#define rtp_scheduler_unlock(sched) g_mutex_unlock((sched)->lock) + +/* void rtp_scheduler_add_set(RtpScheduler *sched, SessionSet *set); */ + +RtpScheduler * ortp_get_scheduler(); +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.c new file mode 100644 index 00000000..7b5ad921 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.c @@ -0,0 +1,187 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <ortp.h> +#include <sessionset.h> +#include "scheduler.h" + +/** + *session_set_init: + *@ss: a SessionSet statically allocated. + * + * Initializes a session set. It is unusefull to call this function on a session set object + * returned by session_set_new(). + * +**/ + + +/** + *session_set_new: + * + * Allocates and initialize a new empty session set. + * + *Returns: the session set. +**/ +SessionSet * session_set_new() +{ + SessionSet *set=g_malloc(sizeof(SessionSet)); + session_set_init(set); + return set; +} + + +/** + *session_set_destroy: + *@set: a SessionSet + * Destroys a session set. + * +**/ + +void session_set_destroy(SessionSet *set) +{ + g_free(set); +} + +gint session_set_and(SessionSet *sched_set, gint maxs, SessionSet *user_set, SessionSet *result_set) +{ + guint32 *mask1,*mask2,*mask3; + gint i=0; + gint j,ret=0; + mask1=(guint32*)&sched_set->rtpset; + mask2=(guint32*)&user_set->rtpset; + mask3=(guint32*)&result_set->rtpset; + while(i<maxs+1){ + *mask3=(*mask1) & (*mask2); /* computes the AND between the two masks*/ + /* and unset the sessions that have been found from the sched_set */ + *mask1=(*mask1) & (~(*mask3)); + if ((*mask3)!=0){ + for (j=0;j<32;j++){ + if ( ((*mask3)>>j) & 1){ + ret++; + } + } + } + i+=32; + mask1++; + mask2++; + mask3++; + } + //printf("session_set_and: ret=%i\n",ret); + return ret; +} + +/** + *session_set_select: + *@recvs: a set of rtp sessions to be watched for read events + *@sends: a set of rtp sessions to be watched for write events + *@errors: a set of rtp sessions to be watched for errors + * + * This function performs similarly as libc select() function, but performs on #RtpSession + * instead of file descriptors. + * session_set_select() suspends the calling process until some events arrive on one of the + * three sets passed in argument. Two of the sets can be NULL. + * The first set @recvs is interpreted as a set of RtpSession waiting for receive events: + * a new buffer (perhaps empty) is availlable on one or more sessions of the set, or the last + * receive operation with rtp_session_recv_with_ts() would have finished if it were in + * blocking mode. + * The second set is interpreted as a set of RtpSession waiting for send events, i.e. the last + * rtp_session_send_with_ts() call on a session would have finished if it were in blocking mode. + * + * When some events arrived on some of sets, then the function returns and sets are changed + * to indicate the sessions where events happened. + * Sessions can be added to sets using session_set_set(), a session has to be tested to be + * part of a set using session_set_is_set(). + * + *Returns: the number of sessions on which the selected events happened. +**/ +int session_set_select(SessionSet *recvs, SessionSet *sends, SessionSet *errors) +{ + gint ret=0,bits; + SessionSet temp; + RtpScheduler *sched=ortp_get_scheduler(); + + /*lock the scheduler to not read the masks while they are being modified by the scheduler*/ + rtp_scheduler_lock(sched); + + while(1){ + /* computes the SessionSet intersection (in the other words mask intersection) between + the mask given by the user and scheduler masks */ + if (recvs!=NULL){ + bits=session_set_and(&sched->r_sessions,sched->all_max,recvs,&temp); + if (bits>0){ + ret+=bits; + /* copy the result set in the given user set */ + session_set_copy(recvs,&temp); + } + } + if (sends!=NULL){ + bits=session_set_and(&sched->w_sessions,sched->all_max,sends,&temp); + if (bits>0){ + ret+=bits; + /* copy the result set in the given user set */ + session_set_copy(sends,&temp); + } + } + if (errors!=NULL){ + bits=session_set_and(&sched->e_sessions,sched->all_max,errors,&temp); + if (bits>0){ + ret+=bits; + /* copy the result set in the given user set */ + session_set_copy(errors,&temp); + } + } + if (ret>0){ + /* there are set file descriptors, return immediately */ + //printf("There are %i sessions set, returning.\n",ret); + rtp_scheduler_unlock(sched); + return ret; + } + //printf("There are %i sessions set.\n",ret); + /* else we wait until the next loop of the scheduler*/ + g_cond_wait(sched->unblock_select_cond,sched->lock); + } + + return -1; +} + +/** + *session_set_set: + *@ss: a set (#SessionSet object) + *@rtpsession: a rtp session + * + * This macro adds rtp session @_session to set @_set. +**/ + +/** + *session_set_is_set: + *@ss: a set (#SessionSet object) + *@rtpsession: a rtp session + * + * This macro tests if @_session is part of @_set. 1 is returned if true, 0 else. +**/ + +/** + *session_set_clr: + *@ss: a set of sessions. + *@rtpsession: a rtp session. + * + * Removes the @_session from the _set. + * + * +**/ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.h new file mode 100644 index 00000000..623b9d10 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/sessionset.h @@ -0,0 +1,102 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SESSIONSET_H +#define SESSIONSET_H + + +#include <rtpsession.h> + + +#ifndef _WIN32 +/* UNIX */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#define ORTP_FD_SET(d, s) FD_SET(d, s) +#define ORTP_FD_CLR(d, s) FD_CLR(d, s) +#define ORTP_FD_ISSET(d, s) FD_ISSET(d, s) +#define ORTP_FD_ZERO(s) FD_ZERO(s) + +typedef fd_set ortp_fd_set; + + +#else +/* WIN32 */ + +#define ORTP_FD_ZERO(s) \ + do { \ + unsigned int __i; \ + ortp_fd_set *__arr = (s); \ + for (__i = 0; __i < sizeof (ortp_fd_set) / sizeof (ortp__fd_mask); ++__i) \ + ORTP__FDS_BITS (__arr)[__i] = 0; \ + } while (0) +#define ORTP_FD_SET(d, s) (ORTP__FDS_BITS (s)[ORTP__FDELT(d)] |= ORTP__FDMASK(d)) +#define ORTP_FD_CLR(d, s) (ORTP__FDS_BITS (s)[ORTP__FDELT(d)] &= ~ORTP__FDMASK(d)) +#define ORTP_FD_ISSET(d, s) ((ORTP__FDS_BITS (s)[ORTP__FDELT(d)] & ORTP__FDMASK(d)) != 0) + + + +/* The fd_set member is required to be an array of longs. */ +typedef long int ortp__fd_mask; + + +/* Number of bits per word of `fd_set' (some code assumes this is 32). */ +#define ORTP__FD_SETSIZE 1024 + +/* It's easier to assume 8-bit bytes than to get CHAR_BIT. */ +#define ORTP__NFDBITS (8 * sizeof (ortp__fd_mask)) +#define ORTP__FDELT(d) ((d) / ORTP__NFDBITS) +#define ORTP__FDMASK(d) ((ortp__fd_mask) 1 << ((d) % ORTP__NFDBITS)) + + +/* fd_set for select and pselect. */ +typedef struct + { + ortp__fd_mask fds_bits[ORTP__FD_SETSIZE / ORTP__NFDBITS]; +# define ORTP__FDS_BITS(set) ((set)->fds_bits) + } ortp_fd_set; + + +#endif /*end WIN32*/ + +struct _SessionSet +{ + ortp_fd_set rtpset; +}; + + +typedef struct _SessionSet SessionSet; + +SessionSet * session_set_new(); +#define session_set_init(ss) ORTP_FD_ZERO(&(ss)->rtpset) +#define session_set_set(ss,rtpsession) ORTP_FD_SET((rtpsession)->mask_pos,&(ss)->rtpset) +#define session_set_is_set(ss,rtpsession) ORTP_FD_ISSET((rtpsession)->mask_pos,&(ss)->rtpset) +#define session_set_clr(ss,rtpsession) ORTP_FD_CLR((rtpsession)->mask_pos,&(ss)->rtpset) + +#define session_set_copy(dest,src) memcpy(&(dest)->rtpset,&(src)->rtpset,sizeof(ortp_fd_set)) + +void session_set_destroy(SessionSet *set); + + +int session_set_select(SessionSet *recvs, SessionSet *sends, SessionSet *errors); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.c new file mode 100644 index 00000000..813ea707 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.c @@ -0,0 +1,297 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <rtpport.h> +#include <rtp.h> +#include <str_utils.h> + +#include <stdio.h> + +void qinit(queue_t *q){ + mblk_init(&q->_q_first); + mblk_init(&q->_q_last); + q->_q_first.b_next=&q->_q_last; + q->_q_last.b_prev=&q->_q_first; + q->q_mcount=0; +} + +void mblk_init(mblk_t *mp) +{ + mp->b_cont=mp->b_prev=mp->b_next=NULL; + mp->b_rptr=mp->b_wptr=NULL; +} + +mblk_t *allocb(int size, int pri) +{ + mblk_t *mp; + dblk_t *datab; + gchar *buf; + + mp=g_malloc(sizeof(mblk_t)); + mblk_init(mp); + datab=g_malloc(sizeof(dblk_t)); + + buf=g_malloc(size); + + datab->db_base=buf; + datab->db_lim=buf+size; + datab->ref_count=1; + datab->db_freefn=g_free; + + mp->b_datap=datab; + mp->b_rptr=mp->b_wptr=buf; + mp->b_next=mp->b_prev=mp->b_cont=NULL; + return mp; +} + +mblk_t *allocb_with_buf(char *buf, int size, int pri, void (*freefn)(void*) ) +{ + mblk_t *mp; + dblk_t *datab; + + mp=g_malloc(sizeof(mblk_t)); + mblk_init(mp); + datab=g_malloc(sizeof(dblk_t)); + + + datab->db_base=buf; + datab->db_lim=buf+size; + datab->ref_count=1; + datab->db_freefn=freefn; + + mp->b_datap=datab; + mp->b_rptr=mp->b_wptr=buf; + mp->b_next=mp->b_prev=mp->b_cont=NULL; + return mp; +} + + +void freeb(mblk_t *mp) +{ + g_return_if_fail(mp->b_datap!=NULL); + g_return_if_fail(mp->b_datap->db_base!=NULL); + + mp->b_datap->ref_count--; + if (mp->b_datap->ref_count==0) + { + if (mp->b_datap->db_freefn!=NULL) + mp->b_datap->db_freefn(mp->b_datap->db_base); + g_free(mp->b_datap); + } + g_free(mp); +} + +void freemsg(mblk_t *mp) +{ + mblk_t *tmp1,*tmp2; + tmp1=mp; + while(tmp1!=NULL) + { + tmp2=tmp1->b_cont; + freeb(tmp1); + tmp1=tmp2; + } +} + +mblk_t *dupb(mblk_t *mp) +{ + mblk_t *newm; + g_return_val_if_fail(mp->b_datap!=NULL,NULL); + g_return_val_if_fail(mp->b_datap->db_base!=NULL,NULL); + + mp->b_datap->ref_count++; + newm=g_malloc(sizeof(mblk_t)); + mblk_init(newm); + newm->b_datap=mp->b_datap; + newm->b_rptr=mp->b_rptr; + newm->b_wptr=mp->b_wptr; + return newm; +} + +/* duplicates a complex mblk_t */ +mblk_t *dupmsg(mblk_t* m) +{ + mblk_t *newm=NULL,*mp,*prev; + prev=newm=dupb(m); + m=m->b_cont; + while (m!=NULL){ + mp=dupb(m); + prev->b_cont=mp; + prev=mp; + m=m->b_cont; + } + return newm; +} + +void putq(queue_t *q,mblk_t *mp) +{ + q->_q_last.b_prev->b_next=mp; + mp->b_prev=q->_q_last.b_prev; + mp->b_next=&q->_q_last; + q->_q_last.b_prev=mp; + q->q_mcount++; +} + +mblk_t *getq(queue_t *q) +{ + mblk_t *tmp; + tmp=q->_q_first.b_next; + if (tmp==&q->_q_last) return NULL; + q->_q_first.b_next=tmp->b_next; + tmp->b_next->b_prev=&q->_q_first; + tmp->b_prev=NULL; + tmp->b_next=NULL; + q->q_mcount--; + return tmp; +} + +/* insert mp in q just before emp */ +void insq(queue_t *q,mblk_t *emp, mblk_t *mp) +{ + if (emp==NULL){ + putq(q,mp); + return; + } + q->q_mcount++; + emp->b_prev->b_next=mp; + mp->b_prev=emp->b_prev; + emp->b_prev=mp; + mp->b_next=emp; +} + +void remq(queue_t *q, mblk_t *mp){ + q->q_mcount--; + mp->b_prev->b_next=mp->b_next; + mp->b_next->b_prev=mp->b_prev; + mp->b_next=NULL; + mp->b_prev=NULL; +} + +/* remove and free all messages in the q */ +void flushq(queue_t *q, int how) +{ + mblk_t *mp; + + while ((mp=getq(q))!=NULL) + { + freemsg(mp); + } +} + +gint msgdsize(mblk_t *mp) +{ + gint msgsize=0; + while(mp!=NULL){ + msgsize+=mp->b_wptr-mp->b_rptr; + mp=mp->b_cont; + } + return msgsize; +} + +mblk_t * msgpullup(mblk_t *mp,int len) +{ + mblk_t *newm; + gint msgsize=msgdsize(mp); + gint rlen; + gint mlen; + + + if ((len==-1) || (len>msgsize)) len=msgsize; + rlen=len; + newm=allocb(len,BPRI_MED); + + while(mp!=NULL){ + mlen=mp->b_wptr-mp->b_rptr; + if (rlen>=mlen) + { + memcpy(newm->b_wptr,mp->b_rptr,mlen); + rlen-=mlen; + newm->b_wptr+=mlen; + } + else /* rlen < mlen */ + { + memcpy(newm->b_wptr,mp->b_rptr,rlen); + newm->b_wptr+=rlen; + + /* put the end of the original message at the end of the new */ + newm->b_cont=dupmsg(mp); + newm->b_cont->b_rptr+=rlen; + return newm; + } + mp=mp->b_cont; + } + return newm; +} + + +mblk_t *copyb(mblk_t *mp) +{ + mblk_t *newm; + gint len=mp->b_wptr-mp->b_rptr; + newm=allocb(len,BPRI_MED); + memcpy(newm->b_wptr,mp->b_rptr,len); + newm->b_wptr+=len; + return newm; +} + +mblk_t *copymsg(mblk_t *mp) +{ + mblk_t *newm=0,*m; + m=newm=copyb(mp); + mp=mp->b_cont; + while(mp!=NULL){ + m->b_cont=copyb(mp); + m=m->b_cont; + mp=mp->b_cont; + } + return newm; +} + +mblk_t * appendb(mblk_t *mp, const char *data, int size, gboolean pad){ + gint padcnt=0; + int i; + if (pad){ + padcnt= (gint)(4L-( (long)(mp->b_wptr+size) % 4L)) % 4L; + } + if ((mp->b_wptr + size +padcnt) > (char*)mp->b_datap->db_lim){ + /* buffer is not large enough: append a new block (with the same size ?)*/ + int plen=(char*)mp->b_datap->db_lim - (char*) mp->b_datap->db_base; + mp->b_cont=allocb(MAX(plen,size),0); + mp=mp->b_cont; + } + if (size) memcpy(mp->b_wptr,data,size); + mp->b_wptr+=size; + for (i=0;i<padcnt;i++){ + mp->b_wptr[0]=0; + mp->b_wptr++; + } + return mp; +} + +void msgappend(mblk_t *mp, const char *data, int size, gboolean pad){ + while(mp->b_cont!=NULL) mp=mp->b_cont; + appendb(mp,data,size,pad); +} + +mblk_t *concatb(mblk_t *mp, mblk_t *newm){ + while (mp->b_cont!=NULL) mp=mp->b_cont; + mp->b_cont=newm; + while(newm->b_cont!=NULL) newm=newm->b_cont; + return newm; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.h new file mode 100644 index 00000000..b605fc2a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/str_utils.h @@ -0,0 +1,118 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef STR_UTILS_H +#define STR_UTILS_H + + +#include <rtpport.h> + + +typedef struct msgb +{ + struct msgb *b_prev; + struct msgb *b_next; + struct msgb *b_cont; + struct datab *b_datap; + char *b_rptr; + char *b_wptr; +} mblk_t; + +typedef struct datab +{ + char *db_base; + char *db_lim; + void (*db_freefn)(void*); + guint ref_count; +} dblk_t; + +typedef struct _queue +{ + mblk_t _q_first; + mblk_t _q_last; + gint q_mcount; /*number of packet in the q */ +} queue_t; + +#ifdef __cplusplus +extern "C" { +#endif + +void qinit(queue_t *q); + +void putq(queue_t *q, mblk_t *m); +mblk_t * getq(queue_t *q); + +void insq(queue_t *q,mblk_t *emp, mblk_t *mp); + +void remq(queue_t *q, mblk_t *mp); + +void mblk_init(mblk_t *mp); + +/* allocates a mblk_t, that points to a datab_t, that points to a buffer of size size. */ +mblk_t *allocb(gint size,gint unused); +#define BPRI_MED 0 + +/* allocates a mblk_t, that points to a datab_t, that points to buf; buf will be freed using freefn */ +mblk_t *allocb_with_buf(char *buf, int size, int pri, void (*freefn)(void*) ); + +/* frees a mblk_t, and if the datab ref_count is 0, frees it and the buffer too */ +void freeb(mblk_t *m); + +/* frees recursively (follow b_cont) a mblk_t, and if the datab +ref_count is 0, frees it and the buffer too */ +void freemsg(mblk_t *mp); + +/* duplicates a mblk_t , buffer is not duplicated*/ +mblk_t *dupb(mblk_t *m); + +/* duplicates a complex mblk_t, buffer is not duplicated */ +mblk_t *dupmsg(mblk_t* m); + +/* remove and free all messages in the q */ +#define FLUSHALL 0 +void flushq(queue_t *q, int how); + +/* returns the size of data of a message */ +gint msgdsize(mblk_t *mp); + +/* concatenates all fragment of a complex message ( a new message is returned, old is untouched*/ +mblk_t * msgpullup(mblk_t *mp,int len); + +/* duplicates a single message, but with buffer included */ +mblk_t *copyb(mblk_t *mp); + +/* duplicates a complex message with buffer included */ +mblk_t *copymsg(mblk_t *mp); + +mblk_t * appendb(mblk_t *mp, const char *data, int size, gboolean pad); +void msgappend(mblk_t *mp, const char *data, int size, gboolean pad); + +mblk_t *concatb(mblk_t *mp, mblk_t *newm); + +#define qempty(q) (&(q)->_q_last==(q)->_q_first.b_next) +#define qfirst(q) ((q)->_q_first.b_next!=&(q)->_q_last ? (q)->_q_first.b_next : NULL) +#define qbegin(q) ((q)->_q_first.b_next) +#define qlast(q) ((q)->_q_last.b_prev!=&(q)->_q_first ? (q)->_q_last.b_prev : NULL) +#define qend(q,mp) ((mp)==&(q)->_q_first || ((mp)==&(q)->_q_last)) +#define qnext(q,mp) ((mp)->b_next) +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.c new file mode 100644 index 00000000..884226ea --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.c @@ -0,0 +1,338 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <telephonyevents.h> + + +PayloadType telephone_event={ + PAYLOAD_AUDIO_PACKETIZED, /*type */ + 8000, /*clock rate */ + 0, /* bytes per sample N/A */ + NULL, /* zero pattern N/A*/ + 0, /*pattern_length N/A */ + 0, /* normal_bitrate */ + "telephone-event", + 0 /*flags */ +}; + +/* tell if the session supports telephony events. For this the telephony events payload_type + must be present in the rtp profile used by the session */ +/** + *rtp_session_telephone_events_supported: + *@session : a rtp session + * + * Tells whether telephony events payload type is supported within the context of the rtp + * session. + * + *Returns: the payload type number used for telephony events if found, -1 if not found. +**/ +gint rtp_session_telephone_events_supported(RtpSession *session) +{ + /* search for a telephony event payload in the current profile */ + session->telephone_events_pt=rtp_profile_get_payload_number_from_mime(session->profile,"telephone-event"); + return session->telephone_events_pt; +} + + +/** + *rtp_session_create_telephone_event_packet: + *@session: a rtp session. + *@start: boolean to indicate if the marker bit should be set. + * + * Allocates a new rtp packet to be used to add named telephony events. The application can use + * then rtp_session_add_telephone_event() to add named events to the packet. + * Finally the packet has to be sent with rtp_session_sendm_with_ts(). + * + *Returns: a message block containing the rtp packet if successfull, NULL if the rtp session + *cannot support telephony event (because the rtp profile it is bound to does not include + *a telephony event payload type). +**/ +mblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start) +{ + mblk_t *mp; + rtp_header_t *rtp; + + g_return_val_if_fail(session->telephone_events_pt!=-1,NULL); + + mp=allocb(RTP_FIXED_HEADER_SIZE+TELEPHONY_EVENTS_ALLOCATED_SIZE,BPRI_MED); + if (mp==NULL) return NULL; + rtp=(rtp_header_t*)mp->b_rptr; + rtp->version = 2; + rtp->markbit=start; + rtp->padbit = 0; + rtp->extbit = 0; + rtp->cc = 0; + rtp->ssrc = session->send_ssrc; + /* timestamp set later, when packet is sended */ + /*seq number set later, when packet is sended */ + + /*set the payload type */ + rtp->paytype=session->telephone_events_pt; + + /*copy the payload */ + mp->b_wptr+=RTP_FIXED_HEADER_SIZE; + return mp; +} + + +/** + *rtp_session_add_telephone_event: + *@session: a rtp session. + *@packet: a rtp packet as a #mblk_t + *@event: the event type as described in rfc2833, ie one of the TEV_ macros. + *@end: boolean to indicate if the end bit should be set. (end of tone) + *@volume: the volume of the telephony tone, as described in rfc2833 + *@duration:the duration of the telephony tone, in timestamp unit. + * + * Adds a named telephony event to a rtp packet previously allocated using + * rtp_session_create_telephone_event_packet(). + * + *Returns 0 on success. +**/ +gint rtp_session_add_telephone_event(RtpSession *session, + mblk_t *packet, guchar event, gint end, guchar volume, guint16 duration) +{ + mblk_t *mp=packet; + telephone_event_t *event_hdr; + + + /* find the place where to add the new telephony event to the packet */ + while(mp->b_cont!=NULL) mp=mp->b_cont; + /* see if we need to allocate a new mblk_t */ + if ( (long)mp->b_wptr >= (long) mp->b_datap->db_lim){ + mblk_t *newm=allocb(TELEPHONY_EVENTS_ALLOCATED_SIZE,BPRI_MED); + mp->b_cont=newm; + mp=mp->b_cont; + } + if (mp==NULL) return -1; + event_hdr=(telephone_event_t*)mp->b_wptr; + event_hdr->event=event; + event_hdr->R=0; + event_hdr->E=end; + event_hdr->volume=volume; + event_hdr->duration=htons(duration); + mp->b_wptr+=sizeof(telephone_event_t); + return 0; +} +/** + *rtp_session_send_dtmf: + *@session : a rtp session + *@dtmf : a character meaning the dtmf (ex: '1', '#' , '9' ...) + *@userts : the timestamp + * + * This functions creates telephony events packets for @dtmf and sends them. + * It uses rtp_session_create_telephone_event_packet() and + * rtp_session_add_telephone_event() to create them and finally + * rtp_session_sendm_with_ts() to send them. + * + *Returns: 0 if successfull, -1 if the session cannot support telephony events or if the dtmf + * given as argument is not valid. +**/ +gint rtp_session_send_dtmf(RtpSession *session, gchar dtmf, guint32 userts) +{ + mblk_t *m1,*m2,*m3; + int tev_type; + /* create the first telephony event packet */ + switch (dtmf){ + case '1': + tev_type=TEV_DTMF_1; + break; + case '2': + tev_type=TEV_DTMF_2; + break; + case '3': + tev_type=TEV_DTMF_3; + break; + case '4': + tev_type=TEV_DTMF_4; + break; + case '5': + tev_type=TEV_DTMF_5; + break; + case '6': + tev_type=TEV_DTMF_6; + break; + case '7': + tev_type=TEV_DTMF_7; + break; + case '8': + tev_type=TEV_DTMF_8; + break; + case '9': + tev_type=TEV_DTMF_9; + break; + case '*': + tev_type=TEV_DTMF_STAR; + break; + case '0': + tev_type=TEV_DTMF_0; + break; + case '#': + tev_type=TEV_DTMF_POUND; + break; + default: + g_warning("Bad dtmf: %c.",dtmf); + return -1; + } + + m1=rtp_session_create_telephone_event_packet(session,1); + if (m1==NULL) return -1; + rtp_session_add_telephone_event(session,m1,tev_type,0,0,160); + /* create a second packet */ + m2=rtp_session_create_telephone_event_packet(session,0); + if (m2==NULL) return -1; + rtp_session_add_telephone_event(session,m2,tev_type,0,0,320); + + /* create a third and final packet */ + m3=rtp_session_create_telephone_event_packet(session,0); + if (m3==NULL) return -1; + rtp_session_add_telephone_event(session,m3,tev_type,1,0,480); + + /* and now sends them */ + rtp_session_sendm_with_ts(session,m1,userts); + rtp_session_sendm_with_ts(session,m2,userts); + /* the last packet is sent three times in order to improve reliability*/ + m1=copymsg(m3); + m2=copymsg(m3); + /* NOTE: */ + /* we need to copymsg() instead of dupmsg() because the buffers are modified when + the packet is sended because of the host-to-network conversion of timestamp,ssrc, csrc, and + seq number. + It could be avoided by making a copy of the buffer when sending physically the packet, but + it add one more copy for every buffer. + Using iomapped socket, it is possible to avoid the user to kernel copy. + */ + rtp_session_sendm_with_ts(session,m3,userts); + rtp_session_sendm_with_ts(session,m1,userts); + rtp_session_sendm_with_ts(session,m2,userts); + return 0; +} + +/** + *rtp_session_read_telephone_event: + *@session: a rtp session from which telephony events are received. + *@packet: a rtp packet as a mblk_t. + *@tab: the address of a pointer. + * + * Reads telephony events from a rtp packet. *@tab points to the beginning of the event buffer. + * + *Returns: the number of events in the packet if successfull, 0 if the packet did not + * contain telephony events. +**/ +gint rtp_session_read_telephone_event(RtpSession *session, + mblk_t *packet,telephone_event_t **tab) +{ + int datasize; + gint num; + int i; + telephone_event_t *tev; + rtp_header_t *hdr=(rtp_header_t*)packet->b_rptr; + g_return_val_if_fail(packet->b_cont!=NULL,-1); + if (hdr->paytype!=session->telephone_events_pt) return 0; /* this is not tel ev.*/ + datasize=msgdsize(packet); + tev=*tab=(telephone_event_t*)packet->b_cont->b_rptr; + /* convert from network to host order what should be */ + num=datasize/sizeof(telephone_event_t); + for (i=0;i<num;i++) + { + tev[i].duration=ntohs(tev[i].duration); + } + return num; +} + + +static void notify_events_ended(RtpSession *session, telephone_event_t *events, int num){ + int i; + for (i=0;i<num;i++){ + if (events[i].E==1){ + rtp_signal_table_emit2(&session->on_telephone_event,(gpointer)(long)events[i].event); + } + } +} + +/* for high level telephony event callback */ +void rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0) +{ + telephone_event_t *events,*evbuf; + int num; + int i; + mblk_t *mp; + rtp_header_t *hdr; + mblk_t *cur_tev; + + hdr=(rtp_header_t*)m0->b_rptr; + mp=m0->b_cont; + + num=(mp->b_wptr-mp->b_rptr)/sizeof(telephone_event_t); + events=(telephone_event_t*)mp->b_rptr; + + + if (hdr->markbit==1) + { + /* this is a start of new events. Store the event buffer for later use*/ + if (session->current_tev!=NULL) { + freemsg(session->current_tev); + session->current_tev=NULL; + } + session->current_tev=copymsg(m0); + /* handle the case where the events are short enough to end within the packet that has the marker bit*/ + notify_events_ended(session,events,num); + } + /* whatever there is a markbit set or not, we parse the packet and compare it to previously received one */ + cur_tev=session->current_tev; + if (cur_tev!=NULL) + { + /* first compare timestamp, they must be identical */ + if (((rtp_header_t*)cur_tev->b_rptr)->timestamp== + ((rtp_header_t*)m0->b_rptr)->timestamp) + { + evbuf=(telephone_event_t*)cur_tev->b_cont; + for (i=0;i<num;i++) + { + if (events[i].E==1) + { + /* update events that have ended */ + if (evbuf[i].E==0){ + evbuf[i].E=1; + /* this is a end of event, report it */ + rtp_signal_table_emit2(&session->on_telephone_event,(gpointer)(long)events[i].event); + } + } + } + } + else + { + /* timestamp are not identical: this is not the same events*/ + if (session->current_tev!=NULL) { + freemsg(session->current_tev); + session->current_tev=NULL; + } + session->current_tev=dupmsg(m0); + } + } + else + { + /* there is no pending events, but we did not received marked bit packet + either the sending implementation is not compliant, either it has been lost, + we must deal with it anyway.*/ + session->current_tev=copymsg(m0); + /* inform the application if there are tone ends */ + notify_events_ended(session,events,num); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.h new file mode 100644 index 00000000..6f6936d5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/telephonyevents.h @@ -0,0 +1,98 @@ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef TELEPHONYEVENTS_H +#define TELEPHONYEVENTS_H + + +#include <rtpsession.h> + + +struct _telephone_event +{ +#ifdef WORDS_BIGENDIAN + guint32 event:8; + guint32 E:1; + guint32 R:1; + guint32 volume:6; + guint32 duration:16; +#else + guint32 event:8; + guint32 volume:6; + guint32 R:1; + guint32 E:1; + guint32 duration:16; +#endif +}; + +typedef struct _telephone_event telephone_event_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern PayloadType telephone_event; + +/* tell if the session supports telephony events. For this the telephony events payload_type + must be present in the rtp profile used by the session */ + +/* low level functions */ +gint rtp_session_telephone_events_supported(RtpSession *session); + +mblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start); + +gint rtp_session_add_telephone_event(RtpSession *session, + mblk_t *packet, guchar event, gint end, guchar volume, guint16 duration); + +gint rtp_session_read_telephone_event(RtpSession *session, + mblk_t *packet,telephone_event_t **tab); + +/* high level functions*/ +gint rtp_session_send_dtmf(RtpSession *session, gchar dtmf, guint32 userts); +/* for high level telephony event callback */ +void rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0); + +#ifdef __cplusplus +} +#endif + +/* the size allocated for telephony events packets */ +#define TELEPHONY_EVENTS_ALLOCATED_SIZE (4*sizeof(telephone_event_t)) + +/* list of named events */ +#define TEV_DTMF_0 (0) +#define TEV_DTMF_1 (1) +#define TEV_DTMF_2 (2) +#define TEV_DTMF_3 (3) +#define TEV_DTMF_4 (4) +#define TEV_DTMF_5 (5) +#define TEV_DTMF_6 (6) +#define TEV_DTMF_7 (7) +#define TEV_DTMF_8 (8) +#define TEV_DTMF_9 (9) +#define TEV_DTMF_STAR (10) +#define TEV_DTMF_POUND (11) +#define TEV_DTMF_A (12) +#define TEV_DTMF_B (13) +#define TEV_DTMF_C (14) +#define TEV_DTMF_D (15) +#define TEV_FLASH (16) + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.c new file mode 100644 index 00000000..1ae601f4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.c @@ -0,0 +1,44 @@ +/*************************************************************************** + * utils.c + * + * Wed Feb 23 14:15:36 2005 + * Copyright 2005 Simon Morlat + * Email simon.morlat@linphone.org + ****************************************************************************/ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <rtpport.h> +#include "utils.h" + +OList *o_list_new(void *data){ + OList *new_elem=g_new0(OList,1); + new_elem->data=data; + return new_elem; +} + +OList * o_list_append(OList *elem, void * data){ + OList *new_elem=o_list_new(data); + OList *it=elem; + if (elem==NULL) return new_elem; + while (it->next!=NULL) it=o_list_next(it); + it->next=new_elem; + new_elem->prev=it; + return elem; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.h new file mode 100644 index 00000000..7b71daf8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/ortp/utils.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * utils.h + * + * Wed Feb 23 14:15:36 2005 + * Copyright 2005 Simon Morlat + * Email simon.morlat@linphone.org + ****************************************************************************/ +/* + The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef UTILS_H +#define UTILS_H + +struct _OList { + struct _OList *next; + struct _OList *prev; + void *data; +}; + +typedef struct _OList OList; + + +#define o_list_next(elem) ((elem)->next) + +OList * o_list_append(OList *elem, void * data); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt new file mode 100644 index 00000000..8b2e72d4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DPOSIX +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${TQT_INCLUDE_DIRS} +) + + +##### cricketxmllite (static) ################### + +tde_add_library( cricketxmllite STATIC_PIC + SOURCES + qname.cc xmlbuilder.cc xmlconstants.cc xmlelement.cc xmlnsstack.cc + xmlparser.cc xmlprinter.cc +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt new file mode 100644 index 00000000..c68cc840 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt @@ -0,0 +1,29 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -DPOSIX +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_BINARY_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cricketxmpp (static) ###################### + +tde_add_library( cricketxmpp STATIC_PIC + SOURCES + constants.cc jid.cc saslmechanism.cc xmppclient.cc xmppengineimpl.cc + xmppengineimpl_iq.cc xmpplogintask.cc xmppstanzaparser.cc xmpptask.cc +) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h index f431b4e5..7a58cd6c 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h @@ -31,6 +31,8 @@ #include "talk/base/linked_ptr.h" #include "talk/base/scoped_ptr.h" +#include <cstring> + namespace buzz { class XmppPasswordImpl { diff --git a/kopete/protocols/jabber/kioslave/CMakeLists.txt b/kopete/protocols/jabber/kioslave/CMakeLists.txt new file mode 100644 index 00000000..12fa4173 --- /dev/null +++ b/kopete/protocols/jabber/kioslave/CMakeLists.txt @@ -0,0 +1,42 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/include + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/jabber + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/xmpp-im + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/cutestuff/util + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES jabberdisco.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kio_jabberdisco (module) ################## + +tde_add_kpart( kio_jabberdisco AUTOMOC + SOURCES jabberdisco.cpp + LINK + jabberclient-static + iris_xmpp_core-static iris_xmpp_im-static iris_jabber-static iris-static + qca-static cutestuff_network-static cutestuff_util-static kio-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/jabber/libiris/CMakeLists.txt b/kopete/protocols/jabber/libiris/CMakeLists.txt new file mode 100644 index 00000000..6c649aa5 --- /dev/null +++ b/kopete/protocols/jabber/libiris/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( iris ) +add_subdirectory( qca ) +add_subdirectory( cutestuff ) diff --git a/kopete/protocols/jabber/libiris/cutestuff/CMakeLists.txt b/kopete/protocols/jabber/libiris/cutestuff/CMakeLists.txt new file mode 100644 index 00000000..4f8cce38 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( network ) +add_subdirectory( util ) diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/CMakeLists.txt b/kopete/protocols/jabber/libiris/cutestuff/network/CMakeLists.txt new file mode 100644 index 00000000..151dd407 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/CMakeLists.txt @@ -0,0 +1,27 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../util + ${CMAKE_CURRENT_SOURCE_DIR}/../../qca/src + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cutestuff_network (static) ################ + +tde_add_library( cutestuff_network STATIC_PIC AUTOMOC + SOURCES + bsocket.cpp httpconnect.cpp httppoll.cpp ndns.cpp servsock.cpp + socks.cpp srvresolver.cpp +) diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/CMakeLists.txt b/kopete/protocols/jabber/libiris/cutestuff/util/CMakeLists.txt new file mode 100644 index 00000000..13d55ca8 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/util/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### cutestuff_util (static) ################### + +tde_add_library( cutestuff_util STATIC_PIC AUTOMOC + SOURCES + base64.cpp bytestream.cpp qrandom.cpp safedelete.cpp sha1.cpp + showtextdlg.cpp +) diff --git a/kopete/protocols/jabber/libiris/iris/CMakeLists.txt b/kopete/protocols/jabber/libiris/iris/CMakeLists.txt new file mode 100644 index 00000000..d5062694 --- /dev/null +++ b/kopete/protocols/jabber/libiris/iris/CMakeLists.txt @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( include ) +add_subdirectory( jabber ) +add_subdirectory( xmpp-core ) +add_subdirectory( xmpp-im ) diff --git a/kopete/protocols/jabber/libiris/iris/include/CMakeLists.txt b/kopete/protocols/jabber/libiris/iris/include/CMakeLists.txt new file mode 100644 index 00000000..ff84643b --- /dev/null +++ b/kopete/protocols/jabber/libiris/iris/include/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### iris (static) ############################# + +tde_moc( SRCS im.h xmpp.h ) + +tde_add_library( iris STATIC_PIC + SOURCES ${SRCS} +) diff --git a/kopete/protocols/jabber/libiris/iris/jabber/CMakeLists.txt b/kopete/protocols/jabber/libiris/iris/jabber/CMakeLists.txt new file mode 100644 index 00000000..e73563f2 --- /dev/null +++ b/kopete/protocols/jabber/libiris/iris/jabber/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../xmpp-im + ${CMAKE_CURRENT_SOURCE_DIR}/../xmpp-core + ${CMAKE_CURRENT_SOURCE_DIR}/../../cutestuff/util + ${CMAKE_CURRENT_SOURCE_DIR}/../../cutestuff/network + ${CMAKE_CURRENT_SOURCE_DIR}/../../qca/src + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### iris_jabber (static) ###################### + +add_custom_command( OUTPUT s5b.moc + COMMAND ${TMOC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/s5b.cpp -o s5b.moc.cpp + COMMAND ${TMOC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/s5b.h -o s5b.moc.h + COMMAND sed -i -e '/^\#include/d' s5b.moc.cpp + COMMAND cat s5b.moc.h s5b.moc.cpp > s5b.moc + DEPENDS s5b.cpp ) + +set_source_files_properties( s5b.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/s5b.moc ) + +tde_add_library( iris_jabber STATIC_PIC AUTOMOC + SOURCES + filetransfer.cpp s5b.cpp xmpp_ibb.cpp xmpp_jidlink.cpp all_mocs.cpp +) diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/CMakeLists.txt b/kopete/protocols/jabber/libiris/iris/xmpp-core/CMakeLists.txt new file mode 100644 index 00000000..4361f30e --- /dev/null +++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../../qca/src + ${CMAKE_CURRENT_SOURCE_DIR}/../../cutestuff/util + ${CMAKE_CURRENT_SOURCE_DIR}/../../cutestuff/network + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### iris_xmpp_core (static) ######################## + +add_custom_command( OUTPUT securestream.moc + COMMAND ${TMOC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/securestream.cpp -o securestream.moc.cpp + COMMAND ${TMOC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/securestream.h -o securestream.moc.h + COMMAND sed -i -e '/^\#include/d' securestream.moc.cpp + COMMAND cat securestream.moc.h securestream.moc.cpp > securestream.moc + DEPENDS securestream.cpp ) + +set_source_files_properties( securestream.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/securestream.moc ) + +tde_add_library( iris_xmpp_core STATIC_PIC + SOURCES + connector.cpp jid.cpp securestream.cpp tlshandler.cpp hash.cpp + protocol.cpp stream.cpp xmlprotocol.cpp parser.cpp simplesasl.cpp +) diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/CMakeLists.txt b/kopete/protocols/jabber/libiris/iris/xmpp-im/CMakeLists.txt new file mode 100644 index 00000000..58a1a034 --- /dev/null +++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/CMakeLists.txt @@ -0,0 +1,37 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../jabber + ${CMAKE_CURRENT_SOURCE_DIR}/../xmpp-core + ${CMAKE_CURRENT_SOURCE_DIR}/../../cutestuff/util + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### iris_xmpp_im (static) ##################### + +add_custom_command( OUTPUT types.moc + COMMAND ${TMOC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp -o types.moc + COMMAND sed -i -e '/^\#include \"/d' types.moc + DEPENDS types.cpp ) + +set_source_files_properties( types.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/types.moc ) + +tde_moc( MOCS xmpp_tasks.h ) + +tde_add_library( iris_xmpp_im STATIC_PIC + SOURCES + client.cpp types.cpp xmpp_tasks.cpp xmpp_vcard.cpp xmpp_xmlcommon.cpp ${MOCS} +) diff --git a/kopete/protocols/jabber/libiris/qca/CMakeLists.txt b/kopete/protocols/jabber/libiris/qca/CMakeLists.txt new file mode 100644 index 00000000..7356f221 --- /dev/null +++ b/kopete/protocols/jabber/libiris/qca/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( src ) diff --git a/kopete/protocols/jabber/libiris/qca/src/CMakeLists.txt b/kopete/protocols/jabber/libiris/qca/src/CMakeLists.txt new file mode 100644 index 00000000..46de2435 --- /dev/null +++ b/kopete/protocols/jabber/libiris/qca/src/CMakeLists.txt @@ -0,0 +1,22 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### qca (static) ############################## + +tde_add_library( qca STATIC_PIC AUTOMOC + SOURCES qca.cpp +) diff --git a/kopete/protocols/jabber/ui/CMakeLists.txt b/kopete/protocols/jabber/ui/CMakeLists.txt new file mode 100644 index 00000000..a4488d62 --- /dev/null +++ b/kopete/protocols/jabber/ui/CMakeLists.txt @@ -0,0 +1,43 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/../../../libkopete/ui + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/include + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/jabber + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/iris/xmpp-im + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/qca/src + ${CMAKE_CURRENT_SOURCE_DIR}/../libiris/cutestuff/util + ${CMAKE_CURRENT_SOURCE_DIR}/../../../libkopete + ${CMAKE_CURRENT_SOURCE_DIR}/../../../libkopete/ui + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetejabberui (static) ################### + +tde_add_library( kopetejabberui STATIC_PIC AUTOMOC + SOURCES + dlgsendraw.ui dlgjabbersendraw.cpp dlgaddcontact.ui + jabberaddcontactpage.cpp dlgvcard.ui dlgjabbervcard.cpp + dlgjabberservices.cpp dlgregister.ui dlgjabberregister.cpp + dlgbrowse.ui dlgjabberbrowse.cpp dlgjabbereditaccountwidget.ui + jabbereditaccountwidget.cpp dlgjabberregisteraccount.ui + jabberregisteraccount.cpp dlgjabberchooseserver.ui + jabberchooseserver.cpp dlgchangepassword.ui + dlgjabberchangepassword.cpp empty.cpp dlgchatroomslist.ui + dlgjabberchatroomslist.cpp dlgchatjoin.ui dlgjabberchatjoin.cpp + dlgservices.ui +) diff --git a/kopete/protocols/meanwhile/CMakeLists.txt b/kopete/protocols/meanwhile/CMakeLists.txt new file mode 100644 index 00000000..994f6e31 --- /dev/null +++ b/kopete/protocols/meanwhile/CMakeLists.txt @@ -0,0 +1,47 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +add_subdirectory( ui ) +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${MEANWHILE_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_meanwhile.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### new_target0 (module) ####################### + +tde_add_kpart( new_target0 AUTOMOC + SOURCES + meanwhileprotocol.cpp meanwhileaddcontactpage.cpp + meanwhileeditaccountwidget.cpp meanwhileaccount.cpp + meanwhilecontact.cpp meanwhilesession.cpp meanwhileplugin.cpp + LINK kopetemeanwhileui-static kopete-shared ${MEANWHILE_LIBRARIES} + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/meanwhile/ConfigureChecks.cmake b/kopete/protocols/meanwhile/ConfigureChecks.cmake new file mode 100644 index 00000000..58d8c67f --- /dev/null +++ b/kopete/protocols/meanwhile/ConfigureChecks.cmake @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +pkg_search_module( MEANWHILE meanwhile ) +if( NOT MEANWHILE_FOUND ) + tde_message_fatal( "meanwhile is required, but was not found on your system" ) +endif( ) diff --git a/kopete/protocols/meanwhile/icons/CMakeLists.txt b/kopete/protocols/meanwhile/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/meanwhile/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/meanwhile/ui/CMakeLists.txt b/kopete/protocols/meanwhile/ui/CMakeLists.txt new file mode 100644 index 00000000..434f849b --- /dev/null +++ b/kopete/protocols/meanwhile/ui/CMakeLists.txt @@ -0,0 +1,27 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetemeanwhileui (static) ################ + +tde_add_library( kopetemeanwhileui STATIC_PIC AUTOMOC + SOURCES + empty.cpp meanwhileeditaccountbase.ui meanwhileaddcontactbase.ui +) diff --git a/kopete/protocols/msn/CMakeLists.txt b/kopete/protocols/msn/CMakeLists.txt new file mode 100644 index 00000000..77bd2cc7 --- /dev/null +++ b/kopete/protocols/msn/CMakeLists.txt @@ -0,0 +1,71 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) +add_subdirectory( icons ) +add_subdirectory( config ) + +if( WITH_WEBCAM ) + add_subdirectory( webcam ) + add_definitions( -DMSN_WEBCAM ) + set( WEBCAM_LIBRARIES mimicwrapper-static kopete_videodevice-shared ${GLIB2_LIBRARIES} ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/webcam + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_msn.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES msnchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_msn ) + + +##### kopete_msn (module) ####################### + +tde_add_kpart( kopete_msn AUTOMOC + SOURCES + dummy.cpp webcam.cpp + LINK kopete_msn_shared-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kopete_msn_shared (shared) ################ + +tde_add_library( kopete_msn_shared SHARED AUTOMOC + SOURCES + msnprotocol.cpp msnaccount.cpp msnaddcontactpage.cpp msncontact.cpp + msnsocket.cpp msnchatsession.cpp msndebugrawcmddlg.cpp + msnnotifysocket.cpp msnswitchboardsocket.cpp + msnfiletransfersocket.cpp msninvitation.cpp sha1.cpp + msnsecureloginhandler.cpp msnchallengehandler.cpp + dispatcher.cpp p2p.cpp messageformatter.cpp incomingtransfer.cpp + outgoingtransfer.cpp webcam.cpp + VERSION 0.0.0 + LINK + kopetemsnui-static ${WEBCAM_LIBRARIES} kopete-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kopete/protocols/msn/config/CMakeLists.txt b/kopete/protocols/msn/config/CMakeLists.txt new file mode 100644 index 00000000..7e0d1337 --- /dev/null +++ b/kopete/protocols/msn/config/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_msn_config.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/kconfiguredialog ) + + +##### kcm_kopete_msn (module) ################### + +tde_add_kpart( kcm_kopete_msn AUTOMOC + SOURCES + msnprefs.ui msnpreferences.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/msn/icons/CMakeLists.txt b/kopete/protocols/msn/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/msn/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/msn/ui/CMakeLists.txt b/kopete/protocols/msn/ui/CMakeLists.txt new file mode 100644 index 00000000..bd70ce80 --- /dev/null +++ b/kopete/protocols/msn/ui/CMakeLists.txt @@ -0,0 +1,29 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetemsnui (static) ###################### + +tde_add_library( kopetemsnui STATIC_PIC AUTOMOC + SOURCES + msnadd.ui msndebugrawcommand_base.ui msninfo.ui msneditaccountui.ui + msneditaccountwidget.cpp +) diff --git a/kopete/protocols/msn/webcam/CMakeLists.txt b/kopete/protocols/msn/webcam/CMakeLists.txt new file mode 100644 index 00000000..aafa2d94 --- /dev/null +++ b/kopete/protocols/msn/webcam/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( libmimic ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIRS} +) + + +##### mimicwrapper (static) ##################### + +tde_add_library( mimicwrapper STATIC_PIC AUTOMOC + SOURCES mimicwrapper.cpp msnwebcamdialog.cpp + LINK mimic-static +) diff --git a/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt b/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt new file mode 100644 index 00000000..d98fc837 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt @@ -0,0 +1,23 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${GLIB2_INCLUDE_DIRS} +) + + +##### mimic (static) ############################ + +tde_add_library( mimic STATIC_PIC + SOURCES + mimic.c encode.c decode.c bitstring.c vlc_common.c vlc_encode.c + vlc_decode.c fdct_quant.c idct_dequant.c colorspace.c deblock.c +) diff --git a/kopete/protocols/oscar/CMakeLists.txt b/kopete/protocols/oscar/CMakeLists.txt new file mode 100644 index 00000000..bbffa100 --- /dev/null +++ b/kopete/protocols/oscar/CMakeLists.txt @@ -0,0 +1,42 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( liboscar ) +add_subdirectory( aim ) +add_subdirectory( icq ) +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/liboscar + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### kopete_oscar (shared) ##################### + +tde_add_library( kopete_oscar SHARED AUTOMOC + SOURCES + oscaraccount.cpp oscarcontact.cpp oscarmyselfcontact.cpp + oscarencodingselectionbase.ui oscarencodingselectiondialog.cpp + oscarlistcontactsbase.ui oscarlistnonservercontacts.cpp + oscarvisibilitybase.ui oscarvisibilitydialog.cpp + oscarversionupdater.cpp + VERSION 2.0.0 + LINK oscar-static kopete-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kopete/protocols/oscar/aim/CMakeLists.txt b/kopete/protocols/oscar/aim/CMakeLists.txt new file mode 100644 index 00000000..75230df3 --- /dev/null +++ b/kopete/protocols/oscar/aim/CMakeLists.txt @@ -0,0 +1,47 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../liboscar + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_aim.desktop aim.protocol + DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_aim (module) ####################### + +tde_add_kpart( kopete_aim AUTOMOC + SOURCES + aimprotocol.cpp aimaccount.cpp aimcontact.cpp aimuserinfo.cpp + aimjoinchat.cpp aimchatsession.cpp + LINK + kopeteaimui-static kopete_oscar-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/oscar/aim/ui/CMakeLists.txt b/kopete/protocols/oscar/aim/ui/CMakeLists.txt new file mode 100644 index 00000000..8f5ff329 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/CMakeLists.txt @@ -0,0 +1,31 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../liboscar + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteaimui (static) ###################### + +tde_add_library( kopeteaimui STATIC_PIC AUTOMOC + SOURCES + aimaddcontactui.ui aimeditaccountui.ui aiminfobase.ui + aimjoinchatbase.ui aimaddcontactpage.cpp aimeditaccountwidget.cpp +) diff --git a/kopete/protocols/oscar/icons/CMakeLists.txt b/kopete/protocols/oscar/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/oscar/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/oscar/icq/CMakeLists.txt b/kopete/protocols/oscar/icq/CMakeLists.txt new file mode 100644 index 00000000..20f8b11a --- /dev/null +++ b/kopete/protocols/oscar/icq/CMakeLists.txt @@ -0,0 +1,45 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../liboscar + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_icq.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES x-icq.desktop DESTINATION ${MIME_INSTALL_DIR}/application ) + + +##### kopete_icq (module) ####################### + +tde_add_kpart( kopete_icq AUTOMOC + SOURCES + icqpresence.cpp icqaccount.cpp icqcontact.cpp icqprotocol.cpp + LINK + kopeteicqui-static kopete_oscar-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/oscar/icq/ui/CMakeLists.txt b/kopete/protocols/oscar/icq/ui/CMakeLists.txt new file mode 100644 index 00000000..9da3ac25 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/CMakeLists.txt @@ -0,0 +1,34 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../liboscar + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteicqui (static) ###################### + +tde_add_library( kopeteicqui STATIC_PIC AUTOMOC + SOURCES + icqadd.ui icqeditaccountui.ui icqeditaccountwidget.cpp + icqgeneralinfo.ui icqotherinfowidget.ui icqworkinfowidget.ui + icqinterestinfowidget.ui icquserinfowidget.cpp icqauthreplyui.ui + icqauthreplydialog.cpp icqaddcontactpage.cpp icqsearchbase.ui + icqsearchdialog.cpp +) diff --git a/kopete/protocols/oscar/liboscar/CMakeLists.txt b/kopete/protocols/oscar/liboscar/CMakeLists.txt new file mode 100644 index 00000000..6053541f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/CMakeLists.txt @@ -0,0 +1,46 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### oscar (static) ############################ + +tde_add_library( oscar STATIC_PIC AUTOMOC + SOURCES + oscarutils.cpp client.cpp task.cpp connector.cpp + inputprotocolbase.cpp coreprotocol.cpp flapprotocol.cpp + snacprotocol.cpp transfer.cpp rtf.cc bytestream.cpp + oscarclientstream.cpp safedelete.cpp stream.cpp oscarconnector.cpp + oscarbytestream.cpp buffer.cpp md5.c logintask.cpp aimlogintask.cpp + icqlogintask.cpp closeconnectiontask.cpp rateclassmanager.cpp + serverversionstask.cpp rateinfotask.cpp errortask.cpp + locationrightstask.cpp profiletask.cpp blmlimitstask.cpp + servicesetuptask.cpp icbmparamstask.cpp ssimanager.cpp rateclass.cpp + rateclass.h prmparamstask.cpp ssiparamstask.cpp ssilisttask.cpp + ssiactivatetask.cpp clientreadytask.cpp senddcinfotask.cpp + sendidletimetask.cpp ownuserinfotask.cpp connection.cpp + onlinenotifiertask.cpp userdetails.cpp ssimodifytask.cpp + oscartypeclasses.cpp oscarmessage.cpp messagereceivertask.cpp + sendmessagetask.cpp icqtask.cpp offlinemessagestask.cpp + ssiauthtask.cpp userinfotask.cpp icquserinfo.cpp icquserinfotask.cpp + usersearchtask.cpp warningtask.cpp changevisibilitytask.cpp + typingnotifytask.cpp buddyicontask.cpp serverredirecttask.cpp + oscarsettings.cpp chatnavservicetask.cpp connectionhandler.cpp + chatservicetask.cpp +) diff --git a/kopete/protocols/sms/CMakeLists.txt b/kopete/protocols/sms/CMakeLists.txt new file mode 100644 index 00000000..017ae01e --- /dev/null +++ b/kopete/protocols/sms/CMakeLists.txt @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) +add_subdirectory( services ) +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/services + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_sms.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_sms (module) ####################### + +tde_add_kpart( kopete_sms AUTOMOC + SOURCES + smsaddcontactpage.cpp smscontact.cpp smseditaccountwidget.cpp + smsprotocol.cpp serviceloader.cpp smsservice.cpp + smsuserpreferences.cpp smsaccount.cpp + LINK + kopetesmsui-static kopetesmsservices-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/sms/icons/CMakeLists.txt b/kopete/protocols/sms/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/sms/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/sms/services/CMakeLists.txt b/kopete/protocols/sms/services/CMakeLists.txt new file mode 100644 index 00000000..c9942bd6 --- /dev/null +++ b/kopete/protocols/sms/services/CMakeLists.txt @@ -0,0 +1,32 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +include( ConfigureChecks.cmake ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetesmsservices (static) ################ + +tde_add_library( kopetesmsservices STATIC_PIC AUTOMOC + SOURCES + smssend.cpp smssendprefs.ui smssendprovider.cpp smsclient.cpp + smsclientprefs.ui gsmlib.cpp gsmlibprefs.ui kopete_unix_serial.cpp + LINK ${GSM_LIBRARY} +) diff --git a/kopete/protocols/sms/services/ConfigureChecks.cmake b/kopete/protocols/sms/services/ConfigureChecks.cmake new file mode 100644 index 00000000..4ed454b4 --- /dev/null +++ b/kopete/protocols/sms/services/ConfigureChecks.cmake @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +if( WITH_GSM ) + + tde_save( CMAKE_REQUIRED_INCLUDES ) + set( CMAKE_REQUIRED_INCLUDES ${TQT_INCLUDE_DIRS} ) + check_include_file_cxx( gsmlib/gsm_util.h HAVE_GSMLIB_GSM_UTIL_H ) + tde_restore( CMAKE_REQUIRED_INCLUDES ) + + if( HAVE_GSMLIB_GSM_UTIL_H ) + set( INCLUDE_SMSGSM 1 ) + find_library( GSM_LIBRARY gsmme ) + endif( ) + + if( NOT (HAVE_GSMLIB_GSM_UTIL_H AND GSM_LIBRARY) ) + tde_message_fatal( "gsmlib is required, but was not found on your system" ) + endif( ) + +endif( ) + +configure_file( config.h.cmake config.h @ONLY ) diff --git a/kopete/protocols/sms/services/config.h.cmake b/kopete/protocols/sms/services/config.h.cmake new file mode 100644 index 00000000..9555d535 --- /dev/null +++ b/kopete/protocols/sms/services/config.h.cmake @@ -0,0 +1 @@ +#cmakedefine INCLUDE_SMSGSM 1 diff --git a/kopete/protocols/sms/ui/CMakeLists.txt b/kopete/protocols/sms/ui/CMakeLists.txt new file mode 100644 index 00000000..619f7a88 --- /dev/null +++ b/kopete/protocols/sms/ui/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetesmsui (static) ###################### + +tde_add_library( kopetesmsui STATIC_PIC AUTOMOC + SOURCES + smsadd.ui smsactprefs.ui smsuserprefs.ui empty.cpp +) diff --git a/kopete/protocols/testbed/CMakeLists.txt b/kopete/protocols/testbed/CMakeLists.txt new file mode 100644 index 00000000..6052845a --- /dev/null +++ b/kopete/protocols/testbed/CMakeLists.txt @@ -0,0 +1,44 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( icons ) +add_subdirectory( ui ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_testbed.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_testbed (module) ################### + +tde_add_kpart( kopete_testbed AUTOMOC + SOURCES + testbedprotocol.cpp testbedcontact.cpp testbedaccount.cpp + testbedaddcontactpage.cpp testbedaddui.ui + testbededitaccountwidget.cpp testbedaccountpreferences.ui + testbedfakeserver.cpp testbedincomingmessage.cpp + LINK kopetetestbedui-static kopete-shared kopete_videodevice-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/testbed/icons/CMakeLists.txt b/kopete/protocols/testbed/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/testbed/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/testbed/testbedaccount.h b/kopete/protocols/testbed/testbedaccount.h index 52261cb7..b58249c6 100644 --- a/kopete/protocols/testbed/testbedaccount.h +++ b/kopete/protocols/testbed/testbedaccount.h @@ -59,7 +59,7 @@ public: /** * 'Connect' to the testbed server. Only sets myself() online. */ - virtual void connect( const Kopete::OnlineStatus& initialStatus = Kopete::OnlineStatus::OnlineStatus() ); + virtual void connect( const Kopete::OnlineStatus& initialStatus = Kopete::OnlineStatus() ); /** * Disconnect from the server. Only sets myself() offline. */ diff --git a/kopete/protocols/testbed/ui/CMakeLists.txt b/kopete/protocols/testbed/ui/CMakeLists.txt new file mode 100644 index 00000000..febe5b7b --- /dev/null +++ b/kopete/protocols/testbed/ui/CMakeLists.txt @@ -0,0 +1,25 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetetestbedui (static) ################## + +tde_add_library( kopetetestbedui STATIC_PIC AUTOMOC + SOURCES testbedwebcamdialog.cpp +) diff --git a/kopete/protocols/winpopup/CMakeLists.txt b/kopete/protocols/winpopup/CMakeLists.txt new file mode 100644 index 00000000..e80e8e51 --- /dev/null +++ b/kopete/protocols/winpopup/CMakeLists.txt @@ -0,0 +1,51 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) +add_subdirectory( icons ) +add_subdirectory( libwinpopup ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/libwinpopup + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_wp.desktop + DESTINATION ${SERVICES_INSTALL_DIR} ) + +install( PROGRAMS + winpopup-send.sh winpopup-install.sh + DESTINATION ${BIN_INSTALL_DIR} ) + + +##### kopete_wp (module) ######################## + +tde_add_kpart( kopete_wp AUTOMOC + SOURCES + wpprotocol.cpp wpcontact.cpp wpaddcontact.cpp wpeditaccount.cpp + wpaccount.cpp wpuserinfo.cpp + LINK + kopetewpui-static winpopup-static kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/winpopup/icons/CMakeLists.txt b/kopete/protocols/winpopup/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/winpopup/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/winpopup/libwinpopup/CMakeLists.txt b/kopete/protocols/winpopup/libwinpopup/CMakeLists.txt new file mode 100644 index 00000000..4f11898b --- /dev/null +++ b/kopete/protocols/winpopup/libwinpopup/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### winpopup (static) ######################### + +tde_add_library( winpopup STATIC_PIC AUTOMOC + SOURCES libwinpopup.cpp +) diff --git a/kopete/protocols/winpopup/ui/CMakeLists.txt b/kopete/protocols/winpopup/ui/CMakeLists.txt new file mode 100644 index 00000000..d73253ce --- /dev/null +++ b/kopete/protocols/winpopup/ui/CMakeLists.txt @@ -0,0 +1,25 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetewpui (static) ####################### + +tde_add_library( kopetewpui STATIC_PIC AUTOMOC + SOURCES + wpaddcontactbase.ui wpeditaccountbase.ui empty.cpp + wpuserinfowidget.ui +) diff --git a/kopete/protocols/yahoo/CMakeLists.txt b/kopete/protocols/yahoo/CMakeLists.txt new file mode 100644 index 00000000..124fc32a --- /dev/null +++ b/kopete/protocols/yahoo/CMakeLists.txt @@ -0,0 +1,49 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( libkyahoo ) +add_subdirectory( ui ) +add_subdirectory( icons ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/libkyahoo + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES yahooconferenceui.rc yahoochatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_yahoo ) +install( FILES kopete_yahoo.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kopete_yahoo (module) ##################### + +tde_add_kpart( kopete_yahoo AUTOMOC + SOURCES + yahooprotocol.cpp yahooeditaccount.cpp yahooaddcontact.cpp + yahooaccount.cpp yahoocontact.cpp yahooconferencemessagemanager.cpp + yahoochatsession.cpp yahooverifyaccount.cpp yahoowebcam.cpp + LINK kyahoo-static kopeteyahooui-static kopeteui-static kopete-shared kopete_videodevice-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/yahoo/icons/CMakeLists.txt b/kopete/protocols/yahoo/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/yahoo/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/yahoo/libkyahoo/CMakeLists.txt b/kopete/protocols/yahoo/libkyahoo/CMakeLists.txt new file mode 100644 index 00000000..a4ba8ba0 --- /dev/null +++ b/kopete/protocols/yahoo/libkyahoo/CMakeLists.txt @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +if( HAVE_INTTYPES_H ) + add_definitions( -DHAVE_INTTYPES_H ) +elseif( HAVE_STDINT_H ) + add_definitions( -DHAVE_STDINT_H ) +endif() + + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kyahoo (static) ########################### + +tde_add_library( kyahoo STATIC_PIC AUTOMOC + SOURCES + client.cpp task.cpp connector.cpp inputprotocolbase.cpp + ymsgprotocol.cpp ymsgtransfer.cpp transfer.cpp + yahoobytestream.cpp bytestream.cpp yahooclientstream.cpp + yahooconnector.cpp safedelete.cpp stream.cpp sha1.c + md5.c crypt.c coreprotocol.cpp logintask.cpp libyahoo.c + yahoo_fn.c listtask.cpp statusnotifiertask.cpp + mailnotifiertask.cpp messagereceivertask.cpp + sendnotifytask.cpp sendmessagetask.cpp logofftask.cpp + changestatustask.cpp modifybuddytask.cpp picturenotifiertask.cpp + requestpicturetask.cpp yahoobuddyiconloader.cpp + stealthtask.cpp sendpicturetask.cpp webcamtask.cpp + conferencetask.cpp sendauthresptask.cpp pingtask.cpp + yabtask.cpp yabentry.cpp modifyyabtask.cpp chatsessiontask.cpp + sendfiletask.cpp filetransfernotifiertask.cpp + receivefiletask.cpp yahoochattask.cpp +) diff --git a/kopete/protocols/yahoo/libkyahoo/ConfigureChecks.cmake b/kopete/protocols/yahoo/libkyahoo/ConfigureChecks.cmake new file mode 100644 index 00000000..e7af0c6c --- /dev/null +++ b/kopete/protocols/yahoo/libkyahoo/ConfigureChecks.cmake @@ -0,0 +1,16 @@ +################################################# +# +# (C) 2010 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +check_include_file( inttypes.h HAVE_INTTYPES_H ) +check_include_file( stdint.h HAVE_STDINT_H ) + +check_include_file( string.h HAVE_STRING_H ) +check_include_file( strings.h HAVE_STRINGS_H ) diff --git a/kopete/protocols/yahoo/ui/CMakeLists.txt b/kopete/protocols/yahoo/ui/CMakeLists.txt new file mode 100644 index 00000000..4da1fb8a --- /dev/null +++ b/kopete/protocols/yahoo/ui/CMakeLists.txt @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopeteyahooui (static) #################### + +tde_add_library( kopeteyahooui STATIC_PIC AUTOMOC + SOURCES + yahooadd.ui yahooeditaccountbase.ui yahooinvitelistbase.ui + yahooinvitelistimpl.cpp empty.cpp yahooverifyaccountbase.ui + yahoostealthsetting.ui yahoowebcamdialog.cpp yahoogeneralinfowidget.ui + yahoouserinfodialog.cpp yahooworkinfowidget.ui + yahoootherinfowidget.ui +) diff --git a/kopete/sounds/CMakeLists.txt b/kopete/sounds/CMakeLists.txt new file mode 100644 index 00000000..5d56e236 --- /dev/null +++ b/kopete/sounds/CMakeLists.txt @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Kopete_Event.ogg Kopete_Received.ogg + Kopete_Sent.ogg Kopete_User_is_Online.ogg + DESTINATION ${SOUND_INSTALL_DIR} ) diff --git a/kopete/styles/CMakeLists.txt b/kopete/styles/CMakeLists.txt new file mode 100644 index 00000000..a98079ab --- /dev/null +++ b/kopete/styles/CMakeLists.txt @@ -0,0 +1,18 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Kopete ) +add_subdirectory( Hacker ) +add_subdirectory( Clean ) +add_subdirectory( Clear ) +add_subdirectory( Konqi ) +add_subdirectory( Retropete ) +add_subdirectory( Gaim ) diff --git a/kopete/styles/Clean/CMakeLists.txt b/kopete/styles/Clean/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Clean/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Clean/Contents/CMakeLists.txt b/kopete/styles/Clean/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Clean/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Clean/Contents/Resources/CMakeLists.txt b/kopete/styles/Clean/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..336beb9f --- /dev/null +++ b/kopete/styles/Clean/Contents/Resources/CMakeLists.txt @@ -0,0 +1,18 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( images ) +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clean/Contents/Resources ) diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Clean/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..589b000e --- /dev/null +++ b/kopete/styles/Clean/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clean/Contents/Resources/Incoming ) diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Clean/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..65bc355f --- /dev/null +++ b/kopete/styles/Clean/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clean/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Clean/Contents/Resources/images/CMakeLists.txt b/kopete/styles/Clean/Contents/Resources/images/CMakeLists.txt new file mode 100644 index 00000000..878bac8d --- /dev/null +++ b/kopete/styles/Clean/Contents/Resources/images/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + action.png important.png internal.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clean/Contents/Resources/images ) diff --git a/kopete/styles/Clear/CMakeLists.txt b/kopete/styles/Clear/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Clear/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Clear/Contents/CMakeLists.txt b/kopete/styles/Clear/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Clear/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Clear/Contents/Resources/CMakeLists.txt b/kopete/styles/Clear/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..a5277126 --- /dev/null +++ b/kopete/styles/Clear/Contents/Resources/CMakeLists.txt @@ -0,0 +1,19 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( images ) +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) +add_subdirectory( Variants ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clear/Contents/Resources ) diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Clear/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..ca799fcb --- /dev/null +++ b/kopete/styles/Clear/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clear/Contents/Resources/Incoming ) diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Clear/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..e5247e97 --- /dev/null +++ b/kopete/styles/Clear/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clear/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Clear/Contents/Resources/Variants/CMakeLists.txt b/kopete/styles/Clear/Contents/Resources/Variants/CMakeLists.txt new file mode 100644 index 00000000..f51a21fb --- /dev/null +++ b/kopete/styles/Clear/Contents/Resources/Variants/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + No_avatars.css + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clear/Contents/Resources/Variants ) diff --git a/kopete/styles/Clear/Contents/Resources/images/CMakeLists.txt b/kopete/styles/Clear/Contents/Resources/images/CMakeLists.txt new file mode 100644 index 00000000..0956fac6 --- /dev/null +++ b/kopete/styles/Clear/Contents/Resources/images/CMakeLists.txt @@ -0,0 +1,25 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + body-background.png footer-outbound-right.png + body-inbound-arrow.png header-inbound-background.png + body-inbound-avatar.png header-inbound-left.png + body-inbound-background.png header-inbound-right.png + body-inbound-left.png header-outbound-background.png + body-inbound-right.png header-outbound-left.png + body-outbound-arrow.png header-outbound-right.png + body-outbound-avatar.png icon-action.png body-outbound-left.png + icon-highlighted.png body-outbound-right.png icon-internal.png + footer-inbound-background.png icon-me.png footer-inbound-left.png + icon-time.png footer-inbound-right.png icon-you.png + footer-outbound-background.png footer-outbound-left.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Clear/Contents/Resources/images ) diff --git a/kopete/styles/Gaim/CMakeLists.txt b/kopete/styles/Gaim/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Gaim/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Gaim/Contents/CMakeLists.txt b/kopete/styles/Gaim/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Gaim/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Gaim/Contents/Resources/CMakeLists.txt b/kopete/styles/Gaim/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..f212a870 --- /dev/null +++ b/kopete/styles/Gaim/Contents/Resources/CMakeLists.txt @@ -0,0 +1,18 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) +add_subdirectory( Variants ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Gaim/Contents/Resources ) diff --git a/kopete/styles/Gaim/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Gaim/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..f4a2302f --- /dev/null +++ b/kopete/styles/Gaim/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Action.html Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Gaim/Contents/Resources/Incoming ) diff --git a/kopete/styles/Gaim/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Gaim/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..32d0ae5d --- /dev/null +++ b/kopete/styles/Gaim/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Action.html Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Gaim/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/CMakeLists.txt b/kopete/styles/Gaim/Contents/Resources/Variants/CMakeLists.txt new file mode 100644 index 00000000..fda30228 --- /dev/null +++ b/kopete/styles/Gaim/Contents/Resources/Variants/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + No-Colors.css Name-Colors.css Status-Colors.css Contact-Colors.css + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Gaim/Contents/Resources/Variants ) diff --git a/kopete/styles/Hacker/CMakeLists.txt b/kopete/styles/Hacker/CMakeLists.txt new file mode 100644 index 00000000..5169384b --- /dev/null +++ b/kopete/styles/Hacker/CMakeLists.txt @@ -0,0 +1,16 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) + +install( FILES + COPYRIGHT README gpl.txt + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker ) diff --git a/kopete/styles/Hacker/Contents/CMakeLists.txt b/kopete/styles/Hacker/Contents/CMakeLists.txt new file mode 100644 index 00000000..08fdf1cc --- /dev/null +++ b/kopete/styles/Hacker/Contents/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) + +install( FILES Info.plist DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents ) diff --git a/kopete/styles/Hacker/Contents/Resources/CMakeLists.txt b/kopete/styles/Hacker/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..bdfe0c38 --- /dev/null +++ b/kopete/styles/Hacker/Contents/Resources/CMakeLists.txt @@ -0,0 +1,19 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( images ) +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) +add_subdirectory( Variants ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents/Resources ) diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Hacker/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..9bdedfce --- /dev/null +++ b/kopete/styles/Hacker/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Content.html Context.html NextContent.html + NextContext.html buddy_icon.png Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents/Resources/Incoming ) diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Hacker/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..bd242686 --- /dev/null +++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Content.html NextContent.html Context.html + NextContext.html buddy_icon.png Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/CMakeLists.txt b/kopete/styles/Hacker/Contents/Resources/Variants/CMakeLists.txt new file mode 100644 index 00000000..b4aac5a5 --- /dev/null +++ b/kopete/styles/Hacker/Contents/Resources/Variants/CMakeLists.txt @@ -0,0 +1,15 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Dark2.css Dark.css Light2.css Light.css Dark2-Noback.css + Dark-Noback.css Light2-Noback.css Light-Noback.css + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents/Resources/Variants ) diff --git a/kopete/styles/Hacker/Contents/Resources/images/CMakeLists.txt b/kopete/styles/Hacker/Contents/Resources/images/CMakeLists.txt new file mode 100644 index 00000000..143190e2 --- /dev/null +++ b/kopete/styles/Hacker/Contents/Resources/images/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + background.png background2.png kopete.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Hacker/Contents/Resources/images ) diff --git a/kopete/styles/Konqi/CMakeLists.txt b/kopete/styles/Konqi/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Konqi/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Konqi/Contents/CMakeLists.txt b/kopete/styles/Konqi/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Konqi/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Konqi/Contents/Resources/CMakeLists.txt b/kopete/styles/Konqi/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..1a69df52 --- /dev/null +++ b/kopete/styles/Konqi/Contents/Resources/CMakeLists.txt @@ -0,0 +1,18 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) +add_subdirectory( Variants ) + +install( FILES + main.css Footer.html Header.html Status.html puce.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Konqi/Contents/Resources ) diff --git a/kopete/styles/Konqi/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Konqi/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..1653fd32 --- /dev/null +++ b/kopete/styles/Konqi/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Konqi/Contents/Resources/Incoming ) diff --git a/kopete/styles/Konqi/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Konqi/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..9b3f366f --- /dev/null +++ b/kopete/styles/Konqi/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Konqi/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/CMakeLists.txt b/kopete/styles/Konqi/Contents/Resources/Variants/CMakeLists.txt new file mode 100644 index 00000000..5aec1cab --- /dev/null +++ b/kopete/styles/Konqi/Contents/Resources/Variants/CMakeLists.txt @@ -0,0 +1,19 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( konqui ) + +install( FILES + Side_blue_moon.css Side_blue_without_transparency.css + Side_green_without_trans.css Side_blue.css + Side_blue_moon_without_transparency.css Side_green.css + Side_green_without_transparency.css + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Konqi/Contents/Resources/Variants ) diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/CMakeLists.txt b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/CMakeLists.txt new file mode 100644 index 00000000..fc87b8ab --- /dev/null +++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/CMakeLists.txt @@ -0,0 +1,16 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + cadre1.png cadre2.png cadre3.png cadre4.png + cadre5.png cadre6.png konqui-blue.png + konqui-green.png konqui-moon.jpg + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Konqi/Contents/Resources/Variants/konqui ) diff --git a/kopete/styles/Kopete/CMakeLists.txt b/kopete/styles/Kopete/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Kopete/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Kopete/Contents/CMakeLists.txt b/kopete/styles/Kopete/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Kopete/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Kopete/Contents/Resources/CMakeLists.txt b/kopete/styles/Kopete/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..ee273f43 --- /dev/null +++ b/kopete/styles/Kopete/Contents/Resources/CMakeLists.txt @@ -0,0 +1,19 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( images ) +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) +add_subdirectory( Variants ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Kopete/Contents/Resources ) diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Kopete/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..03eb154c --- /dev/null +++ b/kopete/styles/Kopete/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Kopete/Contents/Resources/Incoming ) diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Kopete/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..5b1f22e5 --- /dev/null +++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + buddy_icon.png Content.html NextContent.html Action.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Kopete/Contents/Resources/Outgoing ) diff --git a/kopete/styles/Kopete/Contents/Resources/Variants/CMakeLists.txt b/kopete/styles/Kopete/Contents/Resources/Variants/CMakeLists.txt new file mode 100644 index 00000000..62829811 --- /dev/null +++ b/kopete/styles/Kopete/Contents/Resources/Variants/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Big_pictures.css Contact_color.css + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Kopete/Contents/Resources/Variants ) diff --git a/kopete/styles/Kopete/Contents/Resources/images/CMakeLists.txt b/kopete/styles/Kopete/Contents/Resources/images/CMakeLists.txt new file mode 100644 index 00000000..3eb4783d --- /dev/null +++ b/kopete/styles/Kopete/Contents/Resources/images/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + action.png important.png system.png + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Kopete/Contents/Resources/images ) diff --git a/kopete/styles/Retropete/CMakeLists.txt b/kopete/styles/Retropete/CMakeLists.txt new file mode 100644 index 00000000..f08e42e2 --- /dev/null +++ b/kopete/styles/Retropete/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Contents ) diff --git a/kopete/styles/Retropete/Contents/CMakeLists.txt b/kopete/styles/Retropete/Contents/CMakeLists.txt new file mode 100644 index 00000000..88724a18 --- /dev/null +++ b/kopete/styles/Retropete/Contents/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Resources ) diff --git a/kopete/styles/Retropete/Contents/Resources/CMakeLists.txt b/kopete/styles/Retropete/Contents/Resources/CMakeLists.txt new file mode 100644 index 00000000..10f01f85 --- /dev/null +++ b/kopete/styles/Retropete/Contents/Resources/CMakeLists.txt @@ -0,0 +1,17 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( Incoming ) +add_subdirectory( Outgoing ) + +install( FILES + main.css Footer.html Header.html Status.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Retropete/Contents/Resources ) diff --git a/kopete/styles/Retropete/Contents/Resources/Incoming/CMakeLists.txt b/kopete/styles/Retropete/Contents/Resources/Incoming/CMakeLists.txt new file mode 100644 index 00000000..e06e0d1b --- /dev/null +++ b/kopete/styles/Retropete/Contents/Resources/Incoming/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Action.html Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Retropete/Contents/Resources/Incoming ) diff --git a/kopete/styles/Retropete/Contents/Resources/Outgoing/CMakeLists.txt b/kopete/styles/Retropete/Contents/Resources/Outgoing/CMakeLists.txt new file mode 100644 index 00000000..24ef9703 --- /dev/null +++ b/kopete/styles/Retropete/Contents/Resources/Outgoing/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +install( FILES + Action.html Content.html NextContent.html + DESTINATION ${DATA_INSTALL_DIR}/kopete/styles/Retropete/Contents/Resources/Outgoing ) |